Git - Framework SuperObject

fso: / session.php [ Download ]

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<?php

if ( !class_exists('FSO') )
    die('Framework SuperObject base class must be loaded to use its modules!');

/**
 * Database-based sessions
 */

class Session extends FSO_Module
{
    //  Database to use for the sessions
    private $db = null;

    //  Encryption modules
    private $data_crypt, $cookie_crypt;

    //  Configuration
    private $lifetime;

    //  Current data
    private $ip_address, $user_agent;
    private $cookie = array();
    private $data = array();

    //  Garbage collection probability (/100) - chance that old sessions will be removed
    private $gc_prob = 5;

    public function __construct(&$db, $sess_name, $cookie_key, $lifetime = null, $path = null, $domain = null)
    {
        parent::__construct();
        //  Database
        $this->db =& $db;

        //  Module parameters
        $domain = is_null($domain) 
            ? (
                (($pos = strpos($_SERVER['HTTP_HOST'], ':')) !== FALSE) 
                ? substr($_SERVER['HTTP_HOST'], 0, $pos) 
                : $_SERVER['HTTP_HOST']
            ) 
            : $domain;
        $path = is_null($path) ? '/' : $path;
        $this->lifetime = $lifetime = is_null($lifetime) ? ini_get('session.gc_maxlifetime') : $lifetime;

        //  User information
        $ip_address = $_SERVER['REMOTE_ADDR'];
        $user_agent = substr($_SERVER['HTTP_USER_AGENT'], 0, 64);

        //  Cookie encryption
        $this->cookie_crypt =& FSO::make_module('encrypt', array(md5($cookie_key))); 

        //  Try and load an existing session, remove it if its invalid
        if ( isset($_COOKIE[$sess_name]) )
        {
            $cookie = $this->cookie_crypt->decode_var($_COOKIE[$sess_name]);
            if ( !empty($cookie) )
            {
                //  Find the session in the database
                $sess = $this->FSO->db->query('SELECT * FROM `session` WHERE session_id = ?', 
                    $cookie['sid'])->fetch(PDO::FETCH_ASSOC);

                //  Check that the session exists, hasn't expired, and matches the stored info
                if ( !$sess || $ip_address != $sess['ip_address'] ||
                    $user_agent != $sess['user_agent'] ||
                    $sess['last_activity'] < time() - $lifetime )
                {
                    $this->FSO->db->query('DELETE FROM `session` WHERE session_id = ?', $cookie['sid']);
                }
                else
                {
                    $this->cookie = $cookie;
                    $this->data_crypt =& FSO::make_module('encrypt', array($cookie['key']));
                    $this->data = $this->data_crypt->decode_var($sess['data']);
                }
            }
        }

        //  Create a new session if one doesn't already exist
        if ( empty($this->cookie) )
        {
            $this->cookie = array('sid' => $this->new_sid(), 'key' => $this->new_key());
            $this->data_crypt =& FSO::make_module('encrypt', array($this->cookie['key']));
            $this->data = array();
            setcookie($sess_name, $this->cookie_crypt->encode_var($this->cookie), 
                time() + $lifetime, $path, $domain);
            $this->FSO->db->query('INSERT INTO `session` (session_id, ip_address, user_agent, last_activity, data) 
                VALUES (?, ?, ?, ?, ?)', $this->cookie['sid'], $ip_address, $user_agent, time(), 
                $this->data_crypt->encode_var($this->data));
        }

        //  Link to the superglobal
        $_SESSION =& $this->data;

        //  Garbage collection
        $this->gc();
    }

    /**
     * Destructor
     *
     * Save current session information at the end of the script.
     *
     * @return      void
     */
    public function __destruct()
    {
        $this->FSO->db->query('UPDATE `session` SET last_activity = ?, data = ? WHERE session_id = ?',
            time(), $this->data_crypt->encode_var($this->data), $this->cookie['sid']);
    }

    /**
     * Create a new data encryption key
     *
     * @return      string
     */
    private function new_key()
    {
        $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-=_+[]{};\'#:@~`,./<>?';

        $string = '';
        for ( $i = 16; $i > 0; $i-- )
        {
            $string .= $pool[mt_rand(0, strlen($pool) - 1)];
        }
        return md5($string);
    }

    /**
     * Create a new session ID
     *
     * Abuse the MySQL UUID() function to create a new unique ID for a session
     *
     * @return      string
     */
    private function new_sid()
    {
        return $this->FSO->db->query('SELECT UUID()')->fetchColumn(0);
    }

    /**
     * Garbage collection
     *
     * Delete old sessions
     *
     * @return      void
     */
    private function gc()
    {
        srand(time());
        if ((rand() % 100) < $this->gc_prob)
        {   
            $this->FSO->db->query('DELETE FROM `session` WHERE last_activity < ?',
                time() - $this->lifetime);
        }
    }

    /**
     * Member overrides
     *
     * Override $obj->var type calls to use the data array
     */
    public function __get($key) { return $this->data[$key]; }
    public function __set($key, $value) { $this->data[$key] = $value; }
    public function __isset($key) { $this->data[$key] = $value; }
    public function __unset($key) { unset($this->data[$key]); }
}

?>