root/trunk/system/libraries/Session.php

Revision 3700, 9.7 kB (checked in by Shadowhand, 12 days ago)

Updates to trunk:

  • Removed all SYSPATH file checks
  • Deleted some modules: code_coverage (3.0), shoutbox (defunct), user_guide (defunct), kobot (3.0), object_db (3.0)
  • Updated ORM and ORM_Iterator to match 3.0, enabling the new HABTM stuff, see r3636 and r3640
  • Property svn:eol-style set to LF
  • Property copyright set to Copyright (c) 2007 Kohana Team
  • Property svn:keywords set to Id
Line 
1<?php
2/**
3 * Session library.
4 *
5 * $Id$
6 *
7 * @package    Core
8 * @author     Kohana Team
9 * @copyright  (c) 2007-2008 Kohana Team
10 * @license    http://kohanaphp.com/license.html
11 */
12class Session_Core {
13
14    // Session singleton
15    private 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 (self::$instance == NULL)
36        {
37            // Create a new instance
38            new Session;
39        }
40
41        return self::$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 (self::$instance === NULL)
53        {
54            // Load config
55            self::$config = Kohana::config('session');
56
57            // Makes a mirrored array, eg: foo=foo
58            self::$protect = array_combine(self::$protect, self::$protect);
59
60            // Configure garbage collection
61            ini_set('session.gc_probability', (int) self::$config['gc_probability']);
62            ini_set('session.gc_divisor', 100);
63            ini_set('session.gc_maxlifetime', (self::$config['expiration'] == 0) ? 86400 : self::$config['expiration']);
64
65            // Create a new session
66            $this->create();
67
68            if (self::$config['regenerate'] > 0 AND ($_SESSION['total_hits'] % self::$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(self::$config['name'], $_SESSION['session_id'], self::$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            self::$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 (self::$config['driver'] !== 'native')
115        {
116            // Set driver name
117            $driver = 'Session_'.ucfirst(self::$config['driver']).'_Driver';
118
119            // Load the driver
120            if ( ! Kohana::auto_load($driver))
121                throw new Kohana_Exception('core.driver_not_found', self::$config['driver'], get_class($this));
122
123            // Initialize the driver
124            self::$driver = new $driver();
125
126            // Validate the driver
127            if ( ! (self::$driver instanceof Session_Driver))
128                throw new Kohana_Exception('core.driver_implements', self::$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(self::$driver, 'open'),
134                array(self::$driver, 'close'),
135                array(self::$driver, 'read'),
136                array(self::$driver, 'write'),
137                array(self::$driver, 'destroy'),
138                array(self::$driver, 'gc')
139            );
140        }
141
142        // Validate the session name
143        if ( ! preg_match('~^(?=.*[a-z])[a-z0-9_]++$~iD', self::$config['name']))
144            throw new Kohana_Exception('session.invalid_session_name', self::$config['name']);
145
146        // Name the session, this will also be the name of the cookie
147        session_name(self::$config['name']);
148
149        // Set the session cookie parameters
150        session_set_cookie_params
151        (
152            self::$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        self::$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 (self::$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        self::set($vars);
218    }
219
220    /**
221     * Regenerates the global session id.
222     *
223     * @return  void
224     */
225    public function regenerate()
226    {
227        if (self::$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'] = self::$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(self::$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            self::$flash[$key] = 'new';
349            self::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(self::$flash) : func_get_args();
362
363        foreach ($keys as $key)
364        {
365            if (isset(self::$flash[$key]))
366            {
367                self::$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(self::$flash))
386        {
387            foreach (self::$flash as $key => $state)
388            {
389                if ($state === 'old')
390                {
391                    // Flash has expired
392                    unset(self::$flash[$key], $_SESSION[$key]);
393                }
394                else
395                {
396                    // Flash will expire
397                    self::$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 = self::get($key, $default);
433        self::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(self::$protect[$key]))
451                continue;
452
453            // Unset the key
454            unset($_SESSION[$key]);
455        }
456    }
457
458} // End Session Class
Note: See TracBrowser for help on using the browser.