Location: PHPKode > projects > SolarPHP > solar-system-1.1.1/solar/source/solar/Solar/Session.php
<?php
/**
 * 
 * Class for working with the $_SESSION array, including read-once
 * flashes.
 * 
 * On instantiation, lazy-starts the session.  That is, if a session cookie
 * already exists, it starts the session; otherwise, it waits until the 
 * first attempt to write to the session before starting it.
 * 
 * Instantiate this once for each class that wants access to $_SESSION
 * values.  It automatically segments $_SESSION by class name, so be 
 * sure to use setClass() (or the 'class' config key) to identify the
 * segment properly.
 * 
 * A "flash" is a session value that propagates only until it is read,
 * at which time it is removed from the session.  Taken from ideas 
 * popularized by Ruby on Rails, this is useful for forwarding
 * information and messages between page loads without using GET vars
 * or cookies.
 * 
 * @category Solar
 * 
 * @package Solar_Session Session containers with support for named segments,
 * read-once flash values, and lazy-started sessions.
 * 
 * @author Paul M. Jones <hide@address.com>
 * 
 * @author Antti Holvikari <hide@address.com>
 * 
 * @license http://opensource.org/licenses/bsd-license.php BSD
 * 
 * @version $Id: Session.php 4495 2010-03-04 18:44:30Z pmjones $
 * 
 */
class Solar_Session extends Solar_Base
{
    /**
     * 
     * Default configuration values.
     * 
     * @config string class Store values in this top-level key in $_SESSION.  Default is
     *   'Solar'.
     * 
     * @config dependency handler A Solar_Session_Handler dependency injection. Default
     *   is the string 'php', which means to use the native PHP session save.
     *   handler instead of a dependency injection.
     * 
     * @config string P3P Compact [Platform for Privacy Preferences][] policy. Default is
     *   'CP="CAO COR CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT STA"',
     *   which translates to:
     *   
     *   *CAO* _ACCESS Element_: the ability of the individual to view 
     *   identified data and address questions or concerns to the service 
     *   provider. CAO is short for 'contact-and-other', meaning _Identified 
     *   Contact Information and Other Identified Data: access is given to 
     *   identified online and physical contact information as well as to 
     *   certain other identified data._
     * 
     *   *COR* _REMEDIES Element_: Remedies in case a policy breach occurs.
     *   COR is short for 'correct', meaning _Errors or wrongful actions 
     *   arising in connection with the privacy policy will be remedied by 
     *   the service._
     * 
     *   *CURa*
     *   *ADMa*
     *   *DEVa*
     *   *TAIa* _PURPOSE Elements_: Purposes for data processing relevant to 
     *   the Web. The 'a' following each code indicates 'always', meaning 
     *   the site provides no opt-in/opt-out choices for the information 
     *   collected in the _CATEGORIES_ Elements.
     * 
     *   CUR is short for 'current', meaning _Completion and Support of 
     *   Activity For Which Data Was Provided: Information may be used by the 
     *   service provider to complete the activity for which it was provided, 
     *   whether a one-time activity such as returning the results from a Web 
     *   search, forwarding an email message, or placing an order; or a 
     *   recurring activity such as providing a subscription service, or 
     *   allowing access to an online address book or electronic wallet._
     * 
     *   ADM is short for 'admin', meaning _Web Site and System 
     *   Administration: Information may be used for the technical support of 
     *   the Web site and its computer system. This would include processing 
     *   computer account information, information used in the course of 
     *   securing and maintaining the site, and verification of Web site 
     *   activity by the site or its agents._
     * 
     *   DEV is short for 'develop', meaning _Research and Development: 
     *   Information may be used to enhance, evaluate, or otherwise review 
     *   the site, service, product, or market. This does not include personal 
     *   information used to tailor or modify the content to the specific 
     *   individual nor information used to evaluate, target, profile or 
     *   contact the individual._
     * 
     *   TAI is short for 'tailoring', meaning _One-time Tailoring: 
     *   Information may be used to tailor or modify content or design of the 
     *   site where the information is used only for a single visit to the 
     *   site and not used for any kind of future customization. For example, 
     *   an online store might suggest other items a visitor may wish to 
     *   purchase based on the items he has already placed in his shopping 
     *   basket._
     *
     *   *OUR* _RECIPIENT Element_: The legal entity, or domain, beyond the 
     *   service provider and its agents where data may be distributed.
     *   OUR is short for 'ourselves', meaning _Ourselves and/or entities 
     *   acting as our agents or entities for whom we are acting as an agent: 
     *   An agent in this instance is defined as a third party that processes 
     *   data only on behalf of the service provider for the completion of the 
     *   stated purposes. (e.g., the service provider and its printing bureau 
     *   which prints address labels and does nothing further with the 
     *   information.)_
     * 
     *   *BUS*
     *   *IND* _RETENTION Elements_: The type of retention policy in effect.
     *   BUS is short for 'business-practices', meaning _Determined by 
     *   service provider's business practice: Information is retained under 
     *   a service provider's stated business practices._
     * 
     *   IND is short for 'indefinitely', meaning _Information is retained
     *   for an indeterminate period of time._
     *   
     *   *UNI*
     *   *COM*
     *   *NAV*
     *   *INT*
     *   *STA* _CATEGORIES Elements_: Elements inside data elements that 
     *   provide hints to users and user agents as to the intended uses of 
     *   the data.
     * 
     *   UNI is short for 'uniqeid', meaning _Unique Identifiers: 
     *   Non-financial identifiers, excluding government-issued identifiers, 
     *   issued for purposes of consistently identifying or recognizing the 
     *   individual. These include identifiers issued by a Web site or 
     *   service._
     * 
     *   COM is short for 'computer', meaning _Computer Information: 
     *   Information about the computer system that the individual is using 
     *   to access the network -- such as the IP number, domain name, browser 
     *   type or operating system._
     * 
     *   NAV is short for 'navigation', meaning _Navigation and Click-stream 
     *   Data: Data passively generated by browsing the Web site -- such as 
     *   which pages are visited, and how long users stay on each page._
     * 
     *   INT is short for 'interactive', meaning _Interactive Data: Data 
     *   actively generated from or reflecting explicit interactions with a 
     *   service provider through its site -- such as queries to a search 
     *   engine, or logs of account activity._
     * 
     *   STA is short for 'state', meaning _State Management Mechanisms: 
     *   Mechanisms for maintaining a stateful session with a user or 
     *   automatically recognizing users who have visited a particular site 
     *   or accessed particular content previously -- such as HTTP cookies._
     *   
     *   Please refer to the W3C P3P specification for more information 
     *   on customizing this default policy. A compact policy delivered 
     *   in an HTTP header is only part of a complete P3P implementation.
     * 
     *   [Platform for Privacy Preferences]: http://www.w3.org/TR/P3P/
     * 
     * @var array
     * 
     */
    protected $_Solar_Session = array(
        'class'   => 'Solar',
        'handler' => null,
        'P3P'     => 'CP="CAO COR CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT STA"',
    );
    
    /**
     * 
     * The session save handler object.
     * 
     * @var Solar_Session_Handler_Adapter
     * 
     */
    static protected $_handler;
    
    /**
     * 
     * The current request object.
     * 
     * @var Solar_Request
     * 
     */
    static protected $_request;
    
    /**
     * 
     * Array of read-once "flash" keys and values.
     * 
     * Convenience reference to $_SESSION['Solar_Session']['flash'][$this->_class].
     * 
     * @var array
     * 
     */
    protected $_flash = array();
    
    /**
     * 
     * Array of "normal" session keys and values.
     * 
     * Convenience reference to $_SESSION[$this->_class].
     * 
     * @var array
     * 
     */
    protected $_store = array();
    
    /**
     * 
     * The top-level $_SESSION class key for segmenting values.
     * 
     * @var array
     * 
     */
    protected $_class = 'Solar';
    
    /**
     * 
     * Is the $_SESSION loaded for the current class?
     * 
     * @var bool
     * 
     */
    protected $_is_loaded = false;
    
    /**
     * 
     * Post-construction tasks to complete object construction.
     * 
     * @return void
     * 
     */
    protected function _postConstruct()
    {
        parent::_postConstruct();
        
        // only set up the handler if it doesn't exist yet.
        if (! self::$_handler) {
            self::$_handler = Solar::dependency(
                'Solar_Session_Handler',
                $this->_config['handler']
            );
        }
        
        // only set up the request if it doesn't exist yet.
        if (! self::$_request) {
            self::$_request = Solar_Registry::get('request');
        }
        
        // determine the storage segment; use trim() and strict-equals to 
        // allow for string zero segment names.
        $this->_class = trim($this->_config['class']);
        if ($this->_class === '') {
            $this->_class = 'Solar';
        }
        
        // set the class
        $this->setClass($this->_class);
        
        // lazy-start any existing session
        $this->lazyStart();
    }
    
    /**
     * 
     * Magic get for store and flash as a temporary measure.
     * 
     * @param string $key The session key to retrieve.
     * 
     * @return mixed The value of the key.
     * 
     * @deprecated
     * 
     */
    public function &__get($key)
    {
        if ($key == 'store') {
            $this->load();
            return $this->_store;
        }
        
        if ($key == 'flash') {
            $this->load();
            return $this->_flash;
        }
        
        throw $this->_exception('ERR_NO_SUCH_PROPERTY', array(
            'key' => $key,
        ));
    }
    
    /**
     * 
     * Starts the session; automatically sends a P3P header if one is defined
     * (and it is, by default).
     * 
     * @return void
     * 
     */
    public function start()
    {
        // don't start more than once.
        if ($this->isStarted()) {
            // be sure the segment is loaded, though
            $this->load();
            return;
        }
        
        // set the privacy headers
        if ($this->_config['P3P']) {
            $response = Solar_Registry::get('response');
            $response->setHeader('P3P', $this->_config['P3P']);
        }
        
        // start the session
        session_start();
        
        // load the session segment
        $this->load();
    }
    
    /**
     * 
     * Lazy-start the session (i.e., only if a session cookie from the client
     * already exists).
     * 
     * @return void
     * 
     */
    public function lazyStart()
    {
        // don't start more than once.
        if ($this->isStarted()) {
            // be sure the segment is loaded, though
            $this->load();
            return;
        }
        
        $name = session_name();
        if (self::$_request->cookie($name)) {
            // a previous session exists, start it
            $this->start();
        }
    }
    
    /**
     * 
     * Has a session been started yet?
     * 
     * @return bool
     * 
     */
    public function isStarted()
    {
        return session_id() !== '';
    }
    
    /**
     * 
     * Loads the session segment with store and flash values for the current
     * class.
     * 
     * @return void
     * 
     */
    public function load()
    {
        if ($this->isLoaded()) {
            return;
        }
        
        // can't be loaded if the session has started
        if (! $this->isStarted()) {
            // not possible for anything to be loaded, then
            $this->_is_loaded = false;
            $this->_store = array();
            $this->_flash = array();
            return;
        }
        
        // set up the value store.
        if (empty($_SESSION[$this->_class])) {
            $_SESSION[$this->_class] = array();
        }
        $this->_store =& $_SESSION[$this->_class];
        
        // set up the flash store
        if (empty($_SESSION['Solar_Session']['flash'][$this->_class])) {
            $_SESSION['Solar_Session']['flash'][$this->_class] = array();
        }
        $this->_flash =& $_SESSION['Solar_Session']['flash'][$this->_class];
        
        // done!
        $this->_is_loaded = true;
    }
    
    /**
     * 
     * Tells if the session segment is loaded or not.
     * 
     * @return bool
     * 
     */
    public function isLoaded()
    {
        return $this->_is_loaded;
    }
    
    /**
     * 
     * Sets the class segment for $_SESSION; unloads existing store and flash
     * values.
     * 
     * @param string $class The class name to segment by.
     * 
     * @return void
     * 
     */
    public function setClass($class)
    {
        $this->_is_loaded = false;
        $this->_class = $class;
        $this->load();
    }
    
    /**
     * 
     * Gets the current class segment for $_SESSION.
     * 
     * @return string
     * 
     */
    public function getClass()
    {
        return $this->_class;
    }
    
    /**
     * 
     * Whether or not the session currently has a particular data key stored.
     * Does not return or remove the value of the key.
     * 
     * @param string $key The data key.
     * 
     * @return bool True if the session has this data key in it, false if
     * not.
     * 
     */
    public function has($key)
    {
        $this->load();
        return array_key_exists($key, $this->_store);
    }
    
    /**
     * 
     * Sets a normal value by key; this will start the session if needed.
     * 
     * @param string $key The data key.
     * 
     * @param mixed $val The value for the key; previous values will
     * be overwritten.
     * 
     * @return void
     * 
     */
    public function set($key, $val)
    {
        $this->start();
        $this->_store[$key] = $val;
    }
    
    /**
     * 
     * Appends a normal value to a key; this will start the session if needed.
     * 
     * @param string $key The data key.
     * 
     * @param mixed $val The value to add to the key; this will
     * result in the value becoming an array.
     * 
     * @return void
     * 
     */
    public function add($key, $val)
    {
        $this->start();
        
        if (! isset($this->_store[$key])) {
            $this->_store[$key] = array();
        }
        
        if (! is_array($this->_store[$key])) {
            settype($this->_store[$key], 'array');
        }
        
        $this->_store[$key][] = $val;
    }
    
    /**
     * 
     * Gets a normal value by key, or an alternative default value if
     * the key does not exist.
     * 
     * @param string $key The data key.
     * 
     * @param mixed $val If key does not exist, returns this value
     * instead.  Default null.
     * 
     * @return mixed The value.
     * 
     */
    public function get($key, $val = null)
    {
        $this->load();
        
        if (array_key_exists($key, $this->_store)) {
            $val = $this->_store[$key];
        }
        
        return $val;
    }
    
    /**
     * 
     * Deletes a key from the store, removing it entirely.
     * 
     * @param string $key The data key.
     * 
     * @return void
     * 
     */
    public function delete($key)
    {
        // don't start a new session to remove something that isn't there
        $this->lazyStart();
        unset($this->_store[$key]);
    }
    
    /**
     * 
     * Resets (clears) all normal keys and values.
     * 
     * @return void
     * 
     */
    public function reset()
    {
        // don't start a new session to remove something that isn't there
        $this->lazyStart();
        $this->_store = array();
    }
    
    /**
     * 
     * Whether or not the session currently has a particular flash key stored.
     * Does not return or remove the value of the key.
     * 
     * @param string $key The flash key.
     * 
     * @return bool True if the session has this flash key in it, false if
     * not.
     * 
     */
    public function hasFlash($key)
    {
        $this->load();
        return array_key_exists($key, $this->_flash);
    }
    
    /**
     * 
     * Sets a flash value by key; this will start the session if needed.
     * 
     * @param string $key The flash key.
     * 
     * @param mixed $val The value for the key; previous values will
     * be overwritten.
     * 
     * @return void
     * 
     */
    public function setFlash($key, $val)
    {
        $this->start();
        $this->_flash[$key] = $val;
    }
    
    /**
     * 
     * Appends a flash value to a key; this will start the session if needed.
     * 
     * @param string $key The flash key.
     * 
     * @param mixed $val The flash value to add to the key; this will
     * result in the flash becoming an array.
     * 
     * @return void
     * 
     */
    public function addFlash($key, $val)
    {
        $this->start();
        
        if (! isset($this->_flash[$key])) {
            $this->_flash[$key] = array();
        }
        
        if (! is_array($this->_flash[$key])) {
            settype($this->_flash[$key], 'array');
        }
        
        $this->_flash[$key][] = $val;
    }
    
    /**
     * 
     * Gets a flash value by key, thereby removing the value.
     * 
     * @param string $key The flash key.
     * 
     * @param mixed $val If key does not exist, returns this value
     * instead.  Default null.
     * 
     * @return mixed The flash value.
     * 
     * @todo Mike Naberezny notes a possible issue with AJAX requests:
     * 
     *     // If this is an AJAX request, don't clear the flash.
     *     $headers = getallheaders();
     *     if (isset($headers['X-Requested-With']) &&
     *         stripos($headers['X-Requested-With'], 'xmlhttprequest') !== false) {
     *         // leave alone
     *         return;
     *     }
     * 
     * Would need to have Solar_Request access for this to work like the rest
     * of Solar does.
     * 
     */
    public function getFlash($key, $val = null)
    {
        $this->load();
        
        if (array_key_exists($key, $this->_flash)) {
            $val = $this->_flash[$key];
            unset($this->_flash[$key]);
        }
        
        return $val;
    }
    
    /**
     * 
     * Deletes a flash key, removing it entirely.
     * 
     * @param string $key The flash key.
     * 
     * @return void
     * 
     */
    public function deleteFlash($key)
    {
        // don't start a new session to remove something that isn't there
        $this->lazyStart();
        unset($this->_flash[$key]);
    }
    
    /**
     * 
     * Resets (clears) all flash keys and values.
     * 
     * @return void
     * 
     */
    public function resetFlash()
    {
        // don't start a new session to remove something that isn't there
        $this->lazyStart();
        $this->_flash = array();
    }
    
    /**
     * 
     * Resets both "normal" and "flash" values.
     * 
     * @return void
     * 
     */
    public function resetAll()
    {
        $this->reset();
        $this->resetFlash();
    }
    
    /**
     * 
     * Regenerates the session ID.
     * 
     * Use this every time there is a privilege change.
     * 
     * @return void
     * 
     * @see [[php::session_regenerate_id()]]
     * 
     */
    public function regenerateId()
    {
        $this->start();
        if (! headers_sent()) {
            session_regenerate_id(true);
        }
    }
}
Return current item: SolarPHP