Route and Results buttons updated.
[speedfreak] / Server / system / libraries / Session.php
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
2 /**
3  * Session library.
4  *
5  * $Id: Session.php 4073 2009-03-13 17:53:58Z Shadowhand $
6  *
7  * @package    Core
8  * @author     Kohana Team
9  * @copyright  (c) 2007-2008 Kohana Team
10  * @license    http://kohanaphp.com/license.html
11  */
12 class Session_Core {
13
14         // Session singleton
15         protected static $instance;
16
17         // Protected key names (cannot be set by the user)
18         protected static $protect = array('session_id', 'user_agent', 'last_activity', 'ip_address', 'total_hits', '_kf_flash_');
19
20         // Configuration and driver
21         protected static $config;
22         protected static $driver;
23
24         // Flash variables
25         protected static $flash;
26
27         // Input library
28         protected $input;
29
30         /**
31          * Singleton instance of Session.
32          */
33         public static function instance()
34         {
35                 if (Session::$instance == NULL)
36                 {
37                         // Create a new instance
38                         new Session;
39                 }
40
41                 return Session::$instance;
42         }
43
44         /**
45          * On first session instance creation, sets up the driver and creates session.
46          */
47         public function __construct()
48         {
49                 $this->input = Input::instance();
50
51                 // This part only needs to be run once
52                 if (Session::$instance === NULL)
53                 {
54                         // Load config
55                         Session::$config = Kohana::config('session');
56
57                         // Makes a mirrored array, eg: foo=foo
58                         Session::$protect = array_combine(Session::$protect, Session::$protect);
59
60                         // Configure garbage collection
61                         ini_set('session.gc_probability', (int) Session::$config['gc_probability']);
62                         ini_set('session.gc_divisor', 100);
63                         ini_set('session.gc_maxlifetime', (Session::$config['expiration'] == 0) ? 86400 : Session::$config['expiration']);
64
65                         // Create a new session
66                         $this->create();
67
68                         if (Session::$config['regenerate'] > 0 AND ($_SESSION['total_hits'] % Session::$config['regenerate']) === 0)
69                         {
70                                 // Regenerate session id and update session cookie
71                                 $this->regenerate();
72                         }
73                         else
74                         {
75                                 // Always update session cookie to keep the session alive
76                                 cookie::set(Session::$config['name'], $_SESSION['session_id'], Session::$config['expiration']);
77                         }
78
79                         // Close the session just before sending the headers, so that
80                         // the session cookie(s) can be written.
81                         Event::add('system.send_headers', array($this, 'write_close'));
82
83                         // Make sure that sessions are closed before exiting
84                         register_shutdown_function(array($this, 'write_close'));
85
86                         // Singleton instance
87                         Session::$instance = $this;
88                 }
89
90                 Kohana::log('debug', 'Session Library initialized');
91         }
92
93         /**
94          * Get the session id.
95          *
96          * @return  string
97          */
98         public function id()
99         {
100                 return $_SESSION['session_id'];
101         }
102
103         /**
104          * Create a new session.
105          *
106          * @param   array  variables to set after creation
107          * @return  void
108          */
109         public function create($vars = NULL)
110         {
111                 // Destroy any current sessions
112                 $this->destroy();
113
114                 if (Session::$config['driver'] !== 'native')
115                 {
116                         // Set driver name
117                         $driver = 'Session_'.ucfirst(Session::$config['driver']).'_Driver';
118
119                         // Load the driver
120                         if ( ! Kohana::auto_load($driver))
121                                 throw new Kohana_Exception('core.driver_not_found', Session::$config['driver'], get_class($this));
122
123                         // Initialize the driver
124                         Session::$driver = new $driver();
125
126                         // Validate the driver
127                         if ( ! (Session::$driver instanceof Session_Driver))
128                                 throw new Kohana_Exception('core.driver_implements', Session::$config['driver'], get_class($this), 'Session_Driver');
129
130                         // Register non-native driver as the session handler
131                         session_set_save_handler
132                         (
133                                 array(Session::$driver, 'open'),
134                                 array(Session::$driver, 'close'),
135                                 array(Session::$driver, 'read'),
136                                 array(Session::$driver, 'write'),
137                                 array(Session::$driver, 'destroy'),
138                                 array(Session::$driver, 'gc')
139                         );
140                 }
141
142                 // Validate the session name
143                 if ( ! preg_match('~^(?=.*[a-z])[a-z0-9_]++$~iD', Session::$config['name']))
144                         throw new Kohana_Exception('session.invalid_session_name', Session::$config['name']);
145
146                 // Name the session, this will also be the name of the cookie
147                 session_name(Session::$config['name']);
148
149                 // Set the session cookie parameters
150                 session_set_cookie_params
151                 (
152                         Session::$config['expiration'],
153                         Kohana::config('cookie.path'),
154                         Kohana::config('cookie.domain'),
155                         Kohana::config('cookie.secure'),
156                         Kohana::config('cookie.httponly')
157                 );
158
159                 // Start the session!
160                 session_start();
161
162                 // Put session_id in the session variable
163                 $_SESSION['session_id'] = session_id();
164
165                 // Set defaults
166                 if ( ! isset($_SESSION['_kf_flash_']))
167                 {
168                         $_SESSION['total_hits'] = 0;
169                         $_SESSION['_kf_flash_'] = array();
170
171                         $_SESSION['user_agent'] = Kohana::$user_agent;
172                         $_SESSION['ip_address'] = $this->input->ip_address();
173                 }
174
175                 // Set up flash variables
176                 Session::$flash =& $_SESSION['_kf_flash_'];
177
178                 // Increase total hits
179                 $_SESSION['total_hits'] += 1;
180
181                 // Validate data only on hits after one
182                 if ($_SESSION['total_hits'] > 1)
183                 {
184                         // Validate the session
185                         foreach (Session::$config['validate'] as $valid)
186                         {
187                                 switch ($valid)
188                                 {
189                                         // Check user agent for consistency
190                                         case 'user_agent':
191                                                 if ($_SESSION[$valid] !== Kohana::$user_agent)
192                                                         return $this->create();
193                                         break;
194
195                                         // Check ip address for consistency
196                                         case 'ip_address':
197                                                 if ($_SESSION[$valid] !== $this->input->$valid())
198                                                         return $this->create();
199                                         break;
200
201                                         // Check expiration time to prevent users from manually modifying it
202                                         case 'expiration':
203                                                 if (time() - $_SESSION['last_activity'] > ini_get('session.gc_maxlifetime'))
204                                                         return $this->create();
205                                         break;
206                                 }
207                         }
208                 }
209
210                 // Expire flash keys
211                 $this->expire_flash();
212
213                 // Update last activity
214                 $_SESSION['last_activity'] = time();
215
216                 // Set the new data
217                 Session::set($vars);
218         }
219
220         /**
221          * Regenerates the global session id.
222          *
223          * @return  void
224          */
225         public function regenerate()
226         {
227                 if (Session::$config['driver'] === 'native')
228                 {
229                         // Generate a new session id
230                         // Note: also sets a new session cookie with the updated id
231                         session_regenerate_id(TRUE);
232
233                         // Update session with new id
234                         $_SESSION['session_id'] = session_id();
235                 }
236                 else
237                 {
238                         // Pass the regenerating off to the driver in case it wants to do anything special
239                         $_SESSION['session_id'] = Session::$driver->regenerate();
240                 }
241
242                 // Get the session name
243                 $name = session_name();
244
245                 if (isset($_COOKIE[$name]))
246                 {
247                         // Change the cookie value to match the new session id to prevent "lag"
248                         $_COOKIE[$name] = $_SESSION['session_id'];
249                 }
250         }
251
252         /**
253          * Destroys the current session.
254          *
255          * @return  void
256          */
257         public function destroy()
258         {
259                 if (session_id() !== '')
260                 {
261                         // Get the session name
262                         $name = session_name();
263
264                         // Destroy the session
265                         session_destroy();
266
267                         // Re-initialize the array
268                         $_SESSION = array();
269
270                         // Delete the session cookie
271                         cookie::delete($name);
272                 }
273         }
274
275         /**
276          * Runs the system.session_write event, then calls session_write_close.
277          *
278          * @return  void
279          */
280         public function write_close()
281         {
282                 static $run;
283
284                 if ($run === NULL)
285                 {
286                         $run = TRUE;
287
288                         // Run the events that depend on the session being open
289                         Event::run('system.session_write');
290
291                         // Expire flash keys
292                         $this->expire_flash();
293
294                         // Close the session
295                         session_write_close();
296                 }
297         }
298
299         /**
300          * Set a session variable.
301          *
302          * @param   string|array  key, or array of values
303          * @param   mixed         value (if keys is not an array)
304          * @return  void
305          */
306         public function set($keys, $val = FALSE)
307         {
308                 if (empty($keys))
309                         return FALSE;
310
311                 if ( ! is_array($keys))
312                 {
313                         $keys = array($keys => $val);
314                 }
315
316                 foreach ($keys as $key => $val)
317                 {
318                         if (isset(Session::$protect[$key]))
319                                 continue;
320
321                         // Set the key
322                         $_SESSION[$key] = $val;
323                 }
324         }
325
326         /**
327          * Set a flash variable.
328          *
329          * @param   string|array  key, or array of values
330          * @param   mixed         value (if keys is not an array)
331          * @return  void
332          */
333         public function set_flash($keys, $val = FALSE)
334         {
335                 if (empty($keys))
336                         return FALSE;
337
338                 if ( ! is_array($keys))
339                 {
340                         $keys = array($keys => $val);
341                 }
342
343                 foreach ($keys as $key => $val)
344                 {
345                         if ($key == FALSE)
346                                 continue;
347
348                         Session::$flash[$key] = 'new';
349                         Session::set($key, $val);
350                 }
351         }
352
353         /**
354          * Freshen one, multiple or all flash variables.
355          *
356          * @param   string  variable key(s)
357          * @return  void
358          */
359         public function keep_flash($keys = NULL)
360         {
361                 $keys = ($keys === NULL) ? array_keys(Session::$flash) : func_get_args();
362
363                 foreach ($keys as $key)
364                 {
365                         if (isset(Session::$flash[$key]))
366                         {
367                                 Session::$flash[$key] = 'new';
368                         }
369                 }
370         }
371
372         /**
373          * Expires old flash data and removes it from the session.
374          *
375          * @return  void
376          */
377         public function expire_flash()
378         {
379                 static $run;
380
381                 // Method can only be run once
382                 if ($run === TRUE)
383                         return;
384
385                 if ( ! empty(Session::$flash))
386                 {
387                         foreach (Session::$flash as $key => $state)
388                         {
389                                 if ($state === 'old')
390                                 {
391                                         // Flash has expired
392                                         unset(Session::$flash[$key], $_SESSION[$key]);
393                                 }
394                                 else
395                                 {
396                                         // Flash will expire
397                                         Session::$flash[$key] = 'old';
398                                 }
399                         }
400                 }
401
402                 // Method has been run
403                 $run = TRUE;
404         }
405
406         /**
407          * Get a variable. Access to sub-arrays is supported with key.subkey.
408          *
409          * @param   string  variable key
410          * @param   mixed   default value returned if variable does not exist
411          * @return  mixed   Variable data if key specified, otherwise array containing all session data.
412          */
413         public function get($key = FALSE, $default = FALSE)
414         {
415                 if (empty($key))
416                         return $_SESSION;
417
418                 $result = isset($_SESSION[$key]) ? $_SESSION[$key] : Kohana::key_string($_SESSION, $key);
419
420                 return ($result === NULL) ? $default : $result;
421         }
422
423         /**
424          * Get a variable, and delete it.
425          *
426          * @param   string  variable key
427          * @param   mixed   default value returned if variable does not exist
428          * @return  mixed
429          */
430         public function get_once($key, $default = FALSE)
431         {
432                 $return = Session::get($key, $default);
433                 Session::delete($key);
434
435                 return $return;
436         }
437
438         /**
439          * Delete one or more variables.
440          *
441          * @param   string  variable key(s)
442          * @return  void
443          */
444         public function delete($keys)
445         {
446                 $args = func_get_args();
447
448                 foreach ($args as $key)
449                 {
450                         if (isset(Session::$protect[$key]))
451                                 continue;
452
453                         // Unset the key
454                         unset($_SESSION[$key]);
455                 }
456         }
457
458 } // End Session Class