* * @author Antti Holvikari * * @license http://opensource.org/licenses/bsd-license.php BSD * * @version $Id$ * */ class Solar_Session extends Solar_Base { /** * * Default configuration values. * * @config string segment Store values in this top-level key in $_SESSION. Default is * 'Solar'. * * @config string class deprecated synonym for segment. Used only if not null. Default is * null. * * @config dependency manager A Solar_Session_Manager dependency injection. Default * is Solar_Session_Manager_Native which uses php's native session functions for * transferring state between successive requests. * * @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( 'segment' => 'Solar', 'class' => null, 'P3P' => 'CP="CAO COR CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT STA"', 'manager' => 'session_manager', ); /** * * The Manager responsible for tranfering state between requests * * @var Solar_Session_Manager * */ protected $_manager; /** * * Array of read-once "flash" keys and values. * * Convenience reference to $_SESSION['Solar_Session']['flash'][$this->_segment]. * * @var array * */ protected $_flash = array(); /** * * Array of "normal" session keys and values. * * Convenience reference to $_SESSION[$this->_segment]. * * @var array * */ protected $_store = array(); /** * * The top-level $_SESSION class key for segmenting values. * * @var array * */ protected $_segment = '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(); $this->_manager = Solar::dependency( 'Solar_Session_Manager', $this->_config['manager'] ); // Add ourselves to the session manager's list to receive updates $this->_manager->addSession($this); // determine the storage segment; use trim() and strict-equals to // allow for string zero segment names. $segment = is_null($this->_config['class']) ? $this->_config['segment'] : $this->_config['class']; $segment = trim($segment); if ($segment === '') { $segment = 'Solar'; } // set the class $this->setSegment($segment); // 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 $this->_manager->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; } if ($this->_manager->isContinuing()) { // a previous session exists, start it $this->start(); } } /** * * Has a session been started yet? * * @return bool * */ public function isStarted() { return $this->_manager->isStarted(); } /** * * Unloads the session segment after the session has ended * * @return void * */ public function unload() { // cannot unload started sessions if ($this->isStarted()) { return; } $this->_is_loaded = false; $this->_store = array(); $this->_flash = array(); } /** * * 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->unload(); return; } // set up the value store. if (empty($_SESSION[$this->_segment])) { $_SESSION[$this->_segment] = array(); } $this->_store =& $_SESSION[$this->_segment]; // set up the flash store if (empty($_SESSION['Solar_Session']['flash'][$this->_segment])) { $_SESSION['Solar_Session']['flash'][$this->_segment] = array(); } $this->_flash =& $_SESSION['Solar_Session']['flash'][$this->_segment]; // 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 $segment The class name to segment by. * * @return void * */ public function setSegment($segment) { $this->_is_loaded = false; $this->_segment = $segment; $this->load(); } /** * * Gets the current class segment for $_SESSION. * * @return string * */ public function getSegment() { return $this->_segment; } /** * * 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()) { $this->_manager->regenerateId(); } } /** * * Remove this session * * @return bool * */ public function stop() { $this->_manager->stop(); } /** * * Close this session for use in this request, writing the results * to storage for the next request * * @return bool * */ public function close() { $this->_manager->close(); } }