PHP Developers Network

A community of PHP developers offering assistance, advice, discussion, and friendship.
 
Loading
It is currently Mon Sep 28, 2020 2:14 pm

All times are UTC - 5 hours




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Mon Aug 21, 2006 3:36 am 
Offline
The Ninja Space Mod
User avatar

Joined: Fri Aug 05, 2005 1:53 pm
Posts: 6424
Location: Paradise, CA
I built this session class in response to a . This is my first attempt at handling my own session data storage. Much of the session_set_save_handler functionality was borrowed from Jenk's post in that thread. If anything I did was wrong, please correct me. Recommendations, advice, etc. are welcome. Also, none of these classes have been tested... because I have never tested any classes before. I am going to attempt to test them myself, but if I have questions, I intend on asking them in this thread. Thanks for any help given!
Syntax: [ Download ] [ Hide ]
<?php

/**

* Registry class for storing just about anything

*

* This class is basically just a wrapper for an associative array. It

* can be used to store any type of data that an associative array can.

* The most common use for this class is to extend it to allow storage

* in almost any type of class that needs to store data such as

* a session class, an object registry, etc.

*

* @abstract

* @author       Luke Visinoni <luke.visinoni@gmail.com>

* @copyright    Luke Visinoni August 20th 2006

*/


class Registry{



        /**

        * Container for all registered data passed to this class

        * @access protected

        */


        protected $entries = array();

       

        /**

        * Add a value to registry data

        * @access public

        * @param string $key    Associative data array key

        * @param mixed  $value  Associative data array value

        */


        public function register($key, $value){

                $this->entries[$key] = $value;

        }

       

        /**

        * Remove a value from registry data

        * @access public

        * @param string $key    Associative data array key

        */


        public function unregister($key){

                unset($this->entries[$key]);

        }

       

        /**

        * Retrieve a value from registry data

        * @access public

        * @param string $key    Associative data array key

        * @return mixed

        */


        public function get($key){

                return isset($this->entries[$key]) ? $this->entries[$key] : null;

        }

       

        /**

        * Check that a key exists within registry data

        * @access public

        * @param string $key    Associative data array key

        * @return bool

        */


        public function has($key){

                return $name ? isset($this->entries[$key]) : false;

        }

       

        /**

        * Destroy all information stored in this registry

        * @access public

        */


        public function flush(){

                $this->entries = array();

        }

       

        /**

        * Magic wrapper for get()

        * @access public

        * @param string $key    Associative data array key

        * @return mixed

        */


        /*

        public function __get($key){

                return $this->get($key);

        }*/


       

        /**

        * Magic wrapper for register()

        * @access public

        * @param string $key    Associative data array key

        * @param string $value  Associative data array value

        */


        /*

        public function __set($key, $value){

                $this->register($key, $value);

        }*/


}



/**

* Session handler

*

* A class for working with a session. This class is extended

* to provide a particular object or area of an application with its

* own namespace within the session data. This allows for different

* classes to play nicely together and not overwrite or delete session

* data they aren't supposed to.

*

* @author       Luke Visinoni <luke.visinoni@gmail.com>

* @copyright    Luke Visinoni August 20th 2006

*/


class Session extends Registry{

       

        /**

        * Container for session id

        * @access protected

        * @var string

        * @static

        */


        protected static $id = null;

       

        /**

        * This particular instance's namespace

        * @access public

        * @var string

        */


        protected $namespace;                  

       

        /**

        * Set to true if you would like for session's id to be regenerated

        * @access protected

        * @var bool

        */


        protected $regenerate;

       

        /**

        * Constructor

        * @param string $namespace

        * @param bool $regenerate

        */


        public function __construct($namespace='Session', $regenerate=null) {

               

        $this->namespace = $namespace;

        $this->regenerate = $regenerate;

                $this->start();

               

    }

   

        /**

        * Initialize the session

        *

        * This method starts the session. Once the session is started, it tries to correct

        * a known IE problem. (Need to find out more about this problem). Now it checks

        * regenerates the session id, if specefied, and associates entries with our session.

        *

        * @access public

        * @todo research ie problem that the session_cache_limiter is supposed to fix

        */


    public function start(){

                if(session_id() == ''){

                        session_start();

                        if (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')){

                                session_cache_limiter('must-revalidate');

                        }

                        self::$id = session_id();

                }

                if ($this->regenerate){

                        session_regenerate_id();

                }

                $this->entries =& $_SESSION[$this->namespace];

    }

   

        //public function __destruct(){

        //      session_write_close();

        //}

       

        public function __get($key){

                return $this->get($key);

        }

       

        public function __set($key, $value){

                $this->register($key, $value);

        }

}



interface SessionSaveHandler{

        public static function open();

        public static function close();

        public static function read($id);

        public static function write($id, $data);

        public static function destroy($id);

        public static function clean($max);

}



/**

* Session Handler for storing data in a mysql database

*

* @author       Luke Visinoni <luke.visinoni@gmail.com>

* @copyright    Luke Visinoni August 20th 2006

* @todo adjust session_set_save_handler methods so that database type and other factors may be dealt with before write/read/etc.

* @todo Allow for use with other than mysql

*/


class MysqlSession extends Session implements SessionSaveHandler{

       

        /**

        * Holds a mysql connection resource link

        * @access private

        * @var mysql connection resource link

        * @todo Set up error handling to use exceptions

        */


        private static $db;

       

        public function __construct($namespace, $regenerate, $db){

                if(is_null($namespace)) $namespace = 'Session';

                if(is_null($regenerate)) $regenerate = false;

                if(!is_resource($db)){

                        // I still need to learn exception handling, but this works for now.

                        die('Third parameter must be a valid mysql resource link.');

                }

                self::$db =  $db;

                session_set_save_handler(array(&$this,"open"),

                                                                 array(&$this,"close"),

                                                                 array(&$this,"read"),

                                                                 array(&$this,"write"),

                                                                 array(&$this,"destroy"),

                                                                 array(&$this,"clean"));

                //register_shutdown_function('session_write_close');

                parent::__construct($namespace, $regenerate);

        }

        public static function open(){

                return self::$db;

        }

        public static function close(){

                // No need to close my database connection, since it is shared with the rest of the app

                return true;

        }

       

        /**

        * Reads data from the data source

        * @access public

        * @static

        * @returns string

        */


        public static function read($id){

                $id = mysql_real_escape_string($id, self::$db);

         

                $sql = "SELECT `data`

                                FROM   `session`

                                WHERE  `session_id` = '"
. $id . "'";

         

                if ($result = mysql_query($sql, self::$db)) {

                        if (mysql_num_rows($result)) {

                                $record = mysql_fetch_assoc($result);

                                return $record['data'];

                        }

                }

                return '';

        }

       

        /**

        * Writes serialized data to data source

        * @access public

        * @static

        * @returns bool

        */


        public static function write($id, $data){

                $access = time();

         

                $id = mysql_real_escape_string($id, self::$db);

                $access = mysql_real_escape_string($access, self::$db);

                $data = mysql_real_escape_string($data, self::$db);



                $sql = "REPLACE

                                INTO `session` (`session_id`, `access`, `data`)

                                VALUES('"
. $id . "', " . $access . ", '" . $data . "')";

                return mysql_query($sql, self::$db);

        }

       

        /**

        * Delete session data associated with this id

        * @access public

        * @static

        * @returns bool

        */


        public static function destroy($id){

                $id = mysql_real_escape_string($id, self::$db);

         

                $sql = "DELETE

                                FROM   `session`

                                WHERE  `session_id` = '"
. $id . "'";

         

                return mysql_query($sql, self::$db);

        }

       

        /**

        * Delete old session data

        * @access public

        * @static

        * @returns bool

        */


        public static function clean($max){

                $old = time() - $max;

                $old = mysql_real_escape_string($old, self::$db);

         

                $sql = "DELETE

                                FROM   `session`

                                WHERE  `access` < '"
. $old . "'";

         

                return mysql_query($sql, self::$db);

        }

}







// Usage

$DB = new Database('mysql://user:xxxx@localhost/my_database');

// This is kind of a fix for the optional params problem... 90% of the time, I would supply the first argument anyway, so I think this will work just fine

$Session = new MysqlSession(null, null, $DB->getLink());

$Session->register('foo', 'bar');

$Session->magic = "This variable was magically registered";

?>


Last edited by Luke on Tue Aug 22, 2006 3:27 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 21, 2006 3:43 am 
Offline
DevNet Master
User avatar

Joined: Mon Sep 19, 2005 6:24 am
Posts: 3587
Location: London
I'm not too sure on the internals of session_set_save_handler, but you may want to change the array params to:
Syntax: [ Download ] [ Hide ]
array(get_class($this) => 'write')
as the session handling stuff happens after all objects have been destroyed, and even though the manual and other blogs/articles say that you can use objects, I just couldn't get it to work for what I presume is the above reason. Hence why you can't use __destruct()

but session_write_close may negate all that and I may need to read the manual before posting.


EDIT: btw, I stole pretty much my entire class from Chris Shiflett's article on using session_set_save_handler, so credit where it is due. :)


Last edited by Jenk on Mon Aug 21, 2006 3:45 am, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 21, 2006 3:44 am 
Offline
The Ninja Space Mod
User avatar

Joined: Fri Aug 05, 2005 1:53 pm
Posts: 6424
Location: Paradise, CA
it seems to be working alright.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Aug 21, 2006 5:16 am 
Offline
DevNet Resident
User avatar

Joined: Fri Apr 07, 2006 5:13 am
Posts: 1640
Location: Israel


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 5:28 am 
Offline
Forum Newbie
User avatar

Joined: Fri Jan 21, 2005 4:49 am
Posts: 9
Location: Germany


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 5:36 am 
Offline
DevNet Master
User avatar

Joined: Mon Sep 19, 2005 6:24 am
Posts: 3587
Location: London
FYI: you can scratch your @todo for function clean() because $max is the time in miliseconds that a session expires, the PHP engine provides this time.

this is set in the php.ini session.max_lifetime directive :)

Also, for the above post you can now type hint objects (and arrays, but not primitives as yet :():

So this:
Syntax: [ Download ] [ Hide ]
public function __construct($namespace='Session', $regenerate=null, $db){

                if(!is_a($db, 'ADOConnection')){

                        // I still need to learn how to handle exceptions properly, so forgive me if this is not right

                        throw new Exception('Third parameter must be of type: "ADOConnection"');

                }


can become simply this:
Syntax: [ Download ] [ Hide ]
public function __construct($namespace='Session', $regenerate=null, ADOConnection $db){

:)

And furthermore.. when specifying optional arguments like you have done, you should always declare mandatory arguments first:
Syntax: [ Download ] [ Hide ]
public function __construct(ADOConnection $db, $namespace='Session', $regenerate=null){
otherwise your optional args are no longer optional :)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 5:56 am 
Offline
Forum Newbie
User avatar

Joined: Fri Jan 21, 2005 4:49 am
Posts: 9
Location: Germany


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 6:08 am 
Offline
DevNet Master
User avatar

Joined: Mon Sep 19, 2005 6:24 am
Posts: 3587
Location: London
They don't, you receive a fatal error if you pass an argument for $db that isn't of type ADOConnection in that case.

thrown exceptions go with try {} catch () {} blocks, or

try/catch blocks being my preference - and it really is down to preference whether you chose to try/catch every exception, or set a handler.

It would also be better to create sub classed exceptions instead of using Exception class everytime so you can indentify what type of exception is thrown.

PHP5 now has a number of predefined exception types available for use, see for more info (php manual on exceptions) and execute:
Syntax: [ Download ] [ Hide ]
<?php



$classes = get_declared_classes();

$exceptions = "<b>PHP predefined exceptions list:</b><br />\n";



foreach ($classes as $class) {

    if (substr($class, -9) == 'Exception') {

        $exceptions .= $class . "<br />\n";

    }

}



echo $exceptions;

?>


to see a list of predefined exceptions.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 7:11 am 
Offline
Forum Newbie
User avatar

Joined: Fri Jan 21, 2005 4:49 am
Posts: 9
Location: Germany
Point made.. I am with you on the try catch, as it just makes more sense fro me..

I must say that the more I dismantel this class, the more I dislike it.. There is some nice ideas, but some really bad mistakes too. I think that maybe in future a copy and paste and claiming it as your own is not a good way to start a class...

I think I am starting again from scratch.. :roll:


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 7:31 am 
Offline
DevNet Master
User avatar

Joined: Mon Sep 19, 2005 6:24 am
Posts: 3587
Location: London


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 10:12 am 
Offline
The Ninja Space Mod
User avatar

Joined: Fri Aug 05, 2005 1:53 pm
Posts: 6424
Location: Paradise, CA


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 11:38 am 
Offline
Forum Contributor
User avatar

Joined: Sun Jul 09, 2006 1:00 am
Posts: 121
Location: la plata - argentina
Just to know where to put attention...

Are you updating the original class (first comment in this thread) or should we look for new posts below?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 11:46 am 
Offline
The Ninja Space Mod
User avatar

Joined: Fri Aug 05, 2005 1:53 pm
Posts: 6424
Location: Paradise, CA


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 11:51 am 
Offline
Forum Contributor
User avatar

Joined: Sun Jul 09, 2006 1:00 am
Posts: 121
Location: la plata - argentina


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 22, 2006 11:54 am 
Offline
The Ninja Space Mod
User avatar

Joined: Fri Aug 05, 2005 1:53 pm
Posts: 6424
Location: Paradise, CA


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC - 5 hours


Who is online

Users browsing this forum: No registered users and 4 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Jump to:  
Powered by phpBB® Forum Software © phpBB Group