File indexing completed on 2024-04-28 06:00:03

0001 <?php
0002 
0003 /**
0004  * Zend Framework
0005  *
0006  * LICENSE
0007  *
0008  * This source file is subject to the new BSD license that is bundled
0009  * with this package in the file LICENSE.txt.
0010  * It is also available through the world-wide-web at this URL:
0011  * http://framework.zend.com/license/new-bsd
0012  * If you did not receive a copy of the license and are unable to
0013  * obtain it through the world-wide-web, please send an email
0014  * to license@zend.com so we can send you a copy immediately.
0015  *
0016  * @category   Zend
0017  * @package    Zend_Session
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  * @since      Preview Release 0.2
0022  */
0023 
0024 
0025 /**
0026  * @see Zend_Session_Abstract
0027  */
0028 // require_once 'Zend/Session/Abstract.php';
0029 
0030 /**
0031  * @see Zend_Session_Namespace
0032  */
0033 // require_once 'Zend/Session/Namespace.php';
0034 
0035 /**
0036  * @see Zend_Session_SaveHandler_Interface
0037  */
0038 // require_once 'Zend/Session/SaveHandler/Interface.php';
0039 
0040 
0041 /**
0042  * Zend_Session
0043  *
0044  * @category   Zend
0045  * @package    Zend_Session
0046  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0047  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0048  */
0049 class Zend_Session extends Zend_Session_Abstract
0050 {
0051     /**
0052      * Whether or not Zend_Session is being used with unit tests
0053      *
0054      * @internal
0055      * @var bool
0056      */
0057     public static $_unitTestEnabled = false;
0058 
0059     /**
0060      * $_throwStartupException
0061      *
0062      * @var bool|bitset This could also be a combiniation of error codes to catch
0063      */
0064     protected static $_throwStartupExceptions = true;
0065 
0066     /**
0067      * Check whether or not the session was started
0068      *
0069      * @var bool
0070      */
0071     private static $_sessionStarted = false;
0072 
0073     /**
0074      * Whether or not the session id has been regenerated this request.
0075      *
0076      * Id regeneration state
0077      * <0 - regenerate requested when session is started
0078      * 0  - do nothing
0079      * >0 - already called session_regenerate_id()
0080      *
0081      * @var int
0082      */
0083     private static $_regenerateIdState = 0;
0084 
0085     /**
0086      * Private list of php's ini values for ext/session
0087      * null values will default to the php.ini value, otherwise
0088      * the value below will overwrite the default ini value, unless
0089      * the user has set an option explicity with setOptions()
0090      *
0091      * @var array
0092      */
0093     private static $_defaultOptions = array(
0094         'save_path'                 => null,
0095         'name'                      => null, /* this should be set to a unique value for each application */
0096         'save_handler'              => null,
0097         //'auto_start'                => null, /* intentionally excluded (see manual) */
0098         'gc_probability'            => null,
0099         'gc_divisor'                => null,
0100         'gc_maxlifetime'            => null,
0101         'serialize_handler'         => null,
0102         'cookie_lifetime'           => null,
0103         'cookie_path'               => null,
0104         'cookie_domain'             => null,
0105         'cookie_secure'             => null,
0106         'cookie_httponly'           => null,
0107         'use_cookies'               => null,
0108         'use_only_cookies'          => 'on',
0109         'referer_check'             => null,
0110         'entropy_file'              => null,
0111         'entropy_length'            => null,
0112         'cache_limiter'             => null,
0113         'cache_expire'              => null,
0114         'use_trans_sid'             => null,
0115         'bug_compat_42'             => null,
0116         'bug_compat_warn'           => null,
0117         'hash_function'             => null,
0118         'hash_bits_per_character'   => null
0119     );
0120 
0121     /**
0122      * List of options pertaining to Zend_Session that can be set by developers
0123      * using Zend_Session::setOptions(). This list intentionally duplicates
0124      * the individual declaration of static "class" variables by the same names.
0125      *
0126      * @var array
0127      */
0128     private static $_localOptions = array(
0129         'strict'                => '_strict',
0130         'remember_me_seconds'   => '_rememberMeSeconds',
0131         'throw_startup_exceptions' => '_throwStartupExceptions'
0132     );
0133 
0134     /**
0135      * Whether or not write close has been performed.
0136      *
0137      * @var bool
0138      */
0139     private static $_writeClosed = false;
0140 
0141     /**
0142      * Whether or not session id cookie has been deleted
0143      *
0144      * @var bool
0145      */
0146     private static $_sessionCookieDeleted = false;
0147 
0148     /**
0149      * Whether or not session has been destroyed via session_destroy()
0150      *
0151      * @var bool
0152      */
0153     private static $_destroyed = false;
0154 
0155     /**
0156      * Whether or not session must be initiated before usage
0157      *
0158      * @var bool
0159      */
0160     private static $_strict = false;
0161 
0162     /**
0163      * Default number of seconds the session will be remembered for when asked to be remembered
0164      *
0165      * @var int
0166      */
0167     private static $_rememberMeSeconds = 1209600; // 2 weeks
0168 
0169     /**
0170      * Whether the default options listed in Zend_Session::$_localOptions have been set
0171      *
0172      * @var bool
0173      */
0174     private static $_defaultOptionsSet = false;
0175 
0176     /**
0177      * A reference to the set session save handler
0178      *
0179      * @var Zend_Session_SaveHandler_Interface
0180      */
0181     private static $_saveHandler = null;
0182 
0183 
0184     /**
0185      * Constructor overriding - make sure that a developer cannot instantiate
0186      */
0187     protected function __construct()
0188     {
0189     }
0190 
0191 
0192     /**
0193      * setOptions - set both the class specified
0194      *
0195      * @param  array $userOptions - pass-by-keyword style array of <option name, option value> pairs
0196      * @throws Zend_Session_Exception
0197      * @return void
0198      */
0199     public static function setOptions(array $userOptions = array())
0200     {
0201         // set default options on first run only (before applying user settings)
0202         if (!self::$_defaultOptionsSet) {
0203             foreach (self::$_defaultOptions as $defaultOptionName => $defaultOptionValue) {
0204                 if (isset(self::$_defaultOptions[$defaultOptionName])) {
0205                     ini_set("session.$defaultOptionName", $defaultOptionValue);
0206                 }
0207             }
0208 
0209             self::$_defaultOptionsSet = true;
0210         }
0211 
0212         // set the options the user has requested to set
0213         foreach ($userOptions as $userOptionName => $userOptionValue) {
0214 
0215             $userOptionName = strtolower($userOptionName);
0216 
0217             // set the ini based values
0218             if (array_key_exists($userOptionName, self::$_defaultOptions)) {
0219                 ini_set("session.$userOptionName", $userOptionValue);
0220             }
0221             elseif (isset(self::$_localOptions[$userOptionName])) {
0222                 self::${self::$_localOptions[$userOptionName]} = $userOptionValue;
0223             }
0224             else {
0225                 /** @see Zend_Session_Exception */
0226                 // require_once 'Zend/Session/Exception.php';
0227                 throw new Zend_Session_Exception("Unknown option: $userOptionName = $userOptionValue");
0228             }
0229         }
0230     }
0231 
0232     /**
0233      * getOptions()
0234      *
0235      * @param string $optionName OPTIONAL
0236      * @return array|string
0237      */
0238     public static function getOptions($optionName = null)
0239     {
0240         $options = array();
0241         foreach (ini_get_all('session') as $sysOptionName => $sysOptionValues) {
0242             $options[substr($sysOptionName, 8)] = $sysOptionValues['local_value'];
0243         }
0244         foreach (self::$_localOptions as $localOptionName => $localOptionMemberName) {
0245             $options[$localOptionName] = self::${$localOptionMemberName};
0246         }
0247 
0248         if ($optionName) {
0249             if (array_key_exists($optionName, $options)) {
0250                 return $options[$optionName];
0251             }
0252             return null;
0253         }
0254 
0255         return $options;
0256     }
0257 
0258     /**
0259      * setSaveHandler() - Session Save Handler assignment
0260      *
0261      * @param Zend_Session_SaveHandler_Interface $interface
0262      * @throws Zend_Session_Exception When the session_set_save_handler call fails
0263      * @return void
0264      */
0265     public static function setSaveHandler(Zend_Session_SaveHandler_Interface $saveHandler)
0266     {
0267         self::$_saveHandler = $saveHandler;
0268 
0269         if (self::$_unitTestEnabled) {
0270             return;
0271         }
0272 
0273         $result = session_set_save_handler(
0274             array(&$saveHandler, 'open'),
0275             array(&$saveHandler, 'close'),
0276             array(&$saveHandler, 'read'),
0277             array(&$saveHandler, 'write'),
0278             array(&$saveHandler, 'destroy'),
0279             array(&$saveHandler, 'gc')
0280             );
0281 
0282         if (!$result) {
0283             throw new Zend_Session_Exception('Unable to set session handler');
0284         }
0285     }
0286 
0287 
0288     /**
0289      * getSaveHandler() - Get the session Save Handler
0290      *
0291      * @return Zend_Session_SaveHandler_Interface
0292      */
0293     public static function getSaveHandler()
0294     {
0295         return self::$_saveHandler;
0296     }
0297 
0298 
0299     /**
0300      * regenerateId() - Regenerate the session id.  Best practice is to call this after
0301      * session is started.  If called prior to session starting, session id will be regenerated
0302      * at start time.
0303      *
0304      * @throws Zend_Session_Exception
0305      * @return void
0306      */
0307     public static function regenerateId()
0308     {
0309         if (!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {
0310             /** @see Zend_Session_Exception */
0311             // require_once 'Zend/Session/Exception.php';
0312             throw new Zend_Session_Exception("You must call " . __CLASS__ . '::' . __FUNCTION__ .
0313                 "() before any output has been sent to the browser; output started in {$filename}/{$linenum}");
0314         }
0315 
0316         if ( !self::$_sessionStarted ) {
0317             self::$_regenerateIdState = -1;
0318         } else {
0319             if (!self::$_unitTestEnabled) {
0320                 session_regenerate_id(true);
0321             }
0322             self::$_regenerateIdState = 1;
0323         }
0324     }
0325 
0326 
0327     /**
0328      * rememberMe() - Write a persistent cookie that expires after a number of seconds in the future. If no number of
0329      * seconds is specified, then this defaults to self::$_rememberMeSeconds.  Due to clock errors on end users' systems,
0330      * large values are recommended to avoid undesirable expiration of session cookies.
0331      *
0332      * @param int $seconds OPTIONAL specifies TTL for cookie in seconds from present time
0333      * @return void
0334      */
0335     public static function rememberMe($seconds = null)
0336     {
0337         $seconds = (int) $seconds;
0338         $seconds = ($seconds > 0) ? $seconds : self::$_rememberMeSeconds;
0339 
0340         self::rememberUntil($seconds);
0341     }
0342 
0343 
0344     /**
0345      * forgetMe() - Write a volatile session cookie, removing any persistent cookie that may have existed. The session
0346      * would end upon, for example, termination of a web browser program.
0347      *
0348      * @return void
0349      */
0350     public static function forgetMe()
0351     {
0352         self::rememberUntil(0);
0353     }
0354 
0355 
0356     /**
0357      * rememberUntil() - This method does the work of changing the state of the session cookie and making
0358      * sure that it gets resent to the browser via regenerateId()
0359      *
0360      * @param int $seconds
0361      * @return void
0362      */
0363     public static function rememberUntil($seconds = 0)
0364     {
0365         if (self::$_unitTestEnabled) {
0366             self::regenerateId();
0367             return;
0368         }
0369 
0370         $cookieParams = session_get_cookie_params();
0371 
0372         session_set_cookie_params(
0373             $seconds,
0374             $cookieParams['path'],
0375             $cookieParams['domain'],
0376             $cookieParams['secure']
0377             );
0378 
0379         // normally "rememberMe()" represents a security context change, so should use new session id
0380         self::regenerateId();
0381     }
0382 
0383 
0384     /**
0385      * sessionExists() - whether or not a session exists for the current request
0386      *
0387      * @return bool
0388      */
0389     public static function sessionExists()
0390     {
0391         if ((bool)ini_get('session.use_cookies') == true && isset($_COOKIE[session_name()])) {
0392             return true;
0393         } elseif ((bool)ini_get('session.use_only_cookies') == false && isset($_REQUEST[session_name()])) {
0394             return true;
0395         } elseif (self::$_unitTestEnabled) {
0396             return true;
0397         }
0398 
0399         return false;
0400     }
0401 
0402 
0403     /**
0404      * Whether or not session has been destroyed via session_destroy()
0405      *
0406      * @return bool
0407      */
0408     public static function isDestroyed()
0409     {
0410         return self::$_destroyed;
0411     }
0412 
0413 
0414     /**
0415      * start() - Start the session.
0416      *
0417      * @param bool|array $options  OPTIONAL Either user supplied options, or flag indicating if start initiated automatically
0418      * @throws Zend_Session_Exception
0419      * @return void
0420      */
0421     public static function start($options = false)
0422     {
0423         // Check to see if we've been passed an invalid session ID
0424         if ( self::getId() && !self::_checkId(self::getId()) ) {
0425             // Generate a valid, temporary replacement
0426             self::setId(md5(self::getId()));
0427             // Force a regenerate after session is started
0428             self::$_regenerateIdState = -1;
0429         }
0430 
0431         if (self::$_sessionStarted && self::$_destroyed) {
0432             // require_once 'Zend/Session/Exception.php';
0433             throw new Zend_Session_Exception('The session was explicitly destroyed during this request, attempting to re-start is not allowed.');
0434         }
0435 
0436         if (self::$_sessionStarted) {
0437             return; // already started
0438         }
0439 
0440         // make sure our default options (at the least) have been set
0441         if (!self::$_defaultOptionsSet) {
0442             self::setOptions(is_array($options) ? $options : array());
0443         }
0444 
0445         // In strict mode, do not allow auto-starting Zend_Session, such as via "new Zend_Session_Namespace()"
0446         if (self::$_strict && $options === true) {
0447             /** @see Zend_Session_Exception */
0448             // require_once 'Zend/Session/Exception.php';
0449             throw new Zend_Session_Exception('You must explicitly start the session with Zend_Session::start() when session options are set to strict.');
0450         }
0451 
0452         $filename = $linenum = null;
0453         if (!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {
0454             /** @see Zend_Session_Exception */
0455             // require_once 'Zend/Session/Exception.php';
0456             throw new Zend_Session_Exception("Session must be started before any output has been sent to the browser;"
0457                . " output started in {$filename}/{$linenum}");
0458         }
0459 
0460         // See http://www.php.net/manual/en/ref.session.php for explanation
0461         if (!self::$_unitTestEnabled && defined('SID')) {
0462             /** @see Zend_Session_Exception */
0463             // require_once 'Zend/Session/Exception.php';
0464             throw new Zend_Session_Exception('session has already been started by session.auto-start or session_start()');
0465         }
0466 
0467         /**
0468          * Hack to throw exceptions on start instead of php errors
0469          * @see http://framework.zend.com/issues/browse/ZF-1325
0470          */
0471 
0472         $errorLevel = (is_int(self::$_throwStartupExceptions)) ? self::$_throwStartupExceptions : E_ALL;
0473 
0474         /** @see Zend_Session_Exception */
0475         if (!self::$_unitTestEnabled) {
0476 
0477             if (self::$_throwStartupExceptions) {
0478                 // require_once 'Zend/Session/Exception.php';
0479                 set_error_handler(array('Zend_Session_Exception', 'handleSessionStartError'), $errorLevel);
0480             }
0481 
0482             $startedCleanly = session_start();
0483 
0484             if (self::$_throwStartupExceptions) {
0485                 restore_error_handler();
0486             }
0487 
0488             if (!$startedCleanly || Zend_Session_Exception::$sessionStartError != null) {
0489                 if (self::$_throwStartupExceptions) {
0490                     set_error_handler(array('Zend_Session_Exception', 'handleSilentWriteClose'), $errorLevel);
0491                 }
0492                 session_write_close();
0493                 if (self::$_throwStartupExceptions) {
0494                     restore_error_handler();
0495                     throw new Zend_Session_Exception(__CLASS__ . '::' . __FUNCTION__ . '() - ' . Zend_Session_Exception::$sessionStartError);
0496                 }
0497             }
0498         }
0499 
0500         parent::$_readable = true;
0501         parent::$_writable = true;
0502         self::$_sessionStarted = true;
0503         if (self::$_regenerateIdState === -1) {
0504             self::regenerateId();
0505         }
0506 
0507         // run validators if they exist
0508         if (isset($_SESSION['__ZF']['VALID'])) {
0509             self::_processValidators();
0510         }
0511 
0512         self::_processStartupMetadataGlobal();
0513     }
0514 
0515     /**
0516      * Perform a hash-bits check on the session ID
0517      *
0518      * @param string $id Session ID
0519      * @return bool
0520      */
0521     protected static function _checkId($id)
0522     {
0523         $saveHandler = ini_get('session.save_handler');
0524         if ($saveHandler == 'cluster') { // Zend Server SC, validate only after last dash
0525             $dashPos = strrpos($id, '-');
0526             if ($dashPos) {
0527                 $id = substr($id, $dashPos + 1);
0528             }
0529         }
0530 
0531         $hashBitsPerChar = ini_get('session.hash_bits_per_character');
0532         if (!$hashBitsPerChar) {
0533             $hashBitsPerChar = 5; // the default value
0534         }
0535         switch($hashBitsPerChar) {
0536             case 4: $pattern = '^[0-9a-f]*$'; break;
0537             case 5: $pattern = '^[0-9a-v]*$'; break;
0538             case 6: $pattern = '^[0-9a-zA-Z-,]*$'; break;
0539         }
0540         return preg_match('#'.$pattern.'#', $id);
0541     }
0542 
0543 
0544     /**
0545      * _processGlobalMetadata() - this method initizes the sessions GLOBAL
0546      * metadata, mostly global data expiration calculations.
0547      *
0548      * @return void
0549      */
0550     private static function _processStartupMetadataGlobal()
0551     {
0552         // process global metadata
0553         if (isset($_SESSION['__ZF'])) {
0554 
0555             // expire globally expired values
0556             foreach ($_SESSION['__ZF'] as $namespace => $namespace_metadata) {
0557 
0558                 // Expire Namespace by Time (ENT)
0559                 if (isset($namespace_metadata['ENT']) && ($namespace_metadata['ENT'] > 0) && (time() > $namespace_metadata['ENT']) ) {
0560                     unset($_SESSION[$namespace]);
0561                     unset($_SESSION['__ZF'][$namespace]);
0562                 }
0563 
0564                 // Expire Namespace by Global Hop (ENGH) if it wasnt expired above
0565                 if (isset($_SESSION['__ZF'][$namespace]) && isset($namespace_metadata['ENGH']) && $namespace_metadata['ENGH'] >= 1) {
0566 
0567                     $_SESSION['__ZF'][$namespace]['ENGH']--;
0568 
0569                     if ($_SESSION['__ZF'][$namespace]['ENGH'] === 0) {
0570                         if (isset($_SESSION[$namespace])) {
0571                             parent::$_expiringData[$namespace] = $_SESSION[$namespace];
0572                             unset($_SESSION[$namespace]);
0573                         }
0574                         unset($_SESSION['__ZF'][$namespace]);
0575                     }
0576                 }
0577 
0578                 // Expire Namespace Variables by Time (ENVT)
0579                 if (isset($namespace_metadata['ENVT'])) {
0580                     foreach ($namespace_metadata['ENVT'] as $variable => $time) {
0581                         if (time() > $time) {
0582                             unset($_SESSION[$namespace][$variable]);
0583                             unset($_SESSION['__ZF'][$namespace]['ENVT'][$variable]);
0584                         }
0585                     }
0586                     if (empty($_SESSION['__ZF'][$namespace]['ENVT'])) {
0587                         unset($_SESSION['__ZF'][$namespace]['ENVT']);
0588                     }
0589                 }
0590 
0591                 // Expire Namespace Variables by Global Hop (ENVGH)
0592                 if (isset($namespace_metadata['ENVGH'])) {
0593                     foreach ($namespace_metadata['ENVGH'] as $variable => $hops) {
0594                         $_SESSION['__ZF'][$namespace]['ENVGH'][$variable]--;
0595 
0596                         if ($_SESSION['__ZF'][$namespace]['ENVGH'][$variable] === 0) {
0597                             if (isset($_SESSION[$namespace][$variable])) {
0598                                 parent::$_expiringData[$namespace][$variable] = $_SESSION[$namespace][$variable];
0599                                 unset($_SESSION[$namespace][$variable]);
0600                             }
0601                             unset($_SESSION['__ZF'][$namespace]['ENVGH'][$variable]);
0602                         }
0603                     }
0604                     if (empty($_SESSION['__ZF'][$namespace]['ENVGH'])) {
0605                         unset($_SESSION['__ZF'][$namespace]['ENVGH']);
0606                     }
0607                 }
0608                 
0609                 if (isset($namespace) && empty($_SESSION['__ZF'][$namespace])) {
0610                     unset($_SESSION['__ZF'][$namespace]);
0611                 }
0612             }
0613         }
0614 
0615         if (isset($_SESSION['__ZF']) && empty($_SESSION['__ZF'])) {
0616             unset($_SESSION['__ZF']);
0617         }
0618     }
0619 
0620 
0621     /**
0622      * isStarted() - convenience method to determine if the session is already started.
0623      *
0624      * @return bool
0625      */
0626     public static function isStarted()
0627     {
0628         return self::$_sessionStarted;
0629     }
0630 
0631 
0632     /**
0633      * isRegenerated() - convenience method to determine if session_regenerate_id()
0634      * has been called during this request by Zend_Session.
0635      *
0636      * @return bool
0637      */
0638     public static function isRegenerated()
0639     {
0640         return ( (self::$_regenerateIdState > 0) ? true : false );
0641     }
0642 
0643 
0644     /**
0645      * getId() - get the current session id
0646      *
0647      * @return string
0648      */
0649     public static function getId()
0650     {
0651         return session_id();
0652     }
0653 
0654 
0655     /**
0656      * setId() - set an id to a user specified id
0657      *
0658      * @throws Zend_Session_Exception
0659      * @param string $id
0660      * @return void
0661      */
0662     public static function setId($id)
0663     {
0664         if (!self::$_unitTestEnabled && defined('SID')) {
0665             /** @see Zend_Session_Exception */
0666             // require_once 'Zend/Session/Exception.php';
0667             throw new Zend_Session_Exception('The session has already been started.  The session id must be set first.');
0668         }
0669 
0670         if (!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {
0671             /** @see Zend_Session_Exception */
0672             // require_once 'Zend/Session/Exception.php';
0673             throw new Zend_Session_Exception("You must call ".__CLASS__.'::'.__FUNCTION__.
0674                 "() before any output has been sent to the browser; output started in {$filename}/{$linenum}");
0675         }
0676 
0677         if (!is_string($id) || $id === '') {
0678             /** @see Zend_Session_Exception */
0679             // require_once 'Zend/Session/Exception.php';
0680             throw new Zend_Session_Exception('You must provide a non-empty string as a session identifier.');
0681         }
0682 
0683         session_id($id);
0684     }
0685 
0686 
0687     /**
0688      * registerValidator() - register a validator that will attempt to validate this session for
0689      * every future request
0690      *
0691      * @param Zend_Session_Validator_Interface $validator
0692      * @return void
0693      */
0694     public static function registerValidator(Zend_Session_Validator_Interface $validator)
0695     {
0696         $validator->setup();
0697     }
0698 
0699 
0700     /**
0701      * stop() - Disable write access.  Optionally disable read (not implemented).
0702      *
0703      * @return void
0704      */
0705     public static function stop()
0706     {
0707         parent::$_writable = false;
0708     }
0709 
0710 
0711     /**
0712      * writeClose() - Shutdown the sesssion, close writing and detach $_SESSION from the back-end storage mechanism.
0713      * This will complete the internal data transformation on this request.
0714      *
0715      * @param bool $readonly - OPTIONAL remove write access (i.e. throw error if Zend_Session's attempt writes)
0716      * @return void
0717      */
0718     public static function writeClose($readonly = true)
0719     {
0720         if (self::$_unitTestEnabled) {
0721             return;
0722         }
0723 
0724         if (self::$_writeClosed) {
0725             return;
0726         }
0727 
0728         if ($readonly) {
0729             parent::$_writable = false;
0730         }
0731 
0732         session_write_close();
0733         self::$_writeClosed = true;
0734     }
0735 
0736 
0737     /**
0738      * destroy() - This is used to destroy session data, and optionally, the session cookie itself
0739      *
0740      * @param bool $remove_cookie - OPTIONAL remove session id cookie, defaults to true (remove cookie)
0741      * @param bool $readonly - OPTIONAL remove write access (i.e. throw error if Zend_Session's attempt writes)
0742      * @return void
0743      */
0744     public static function destroy($remove_cookie = true, $readonly = true)
0745     {
0746         if (self::$_unitTestEnabled) {
0747             return;
0748         }
0749 
0750         if (self::$_destroyed) {
0751             return;
0752         }
0753 
0754         if ($readonly) {
0755             parent::$_writable = false;
0756         }
0757 
0758         session_destroy();
0759         self::$_destroyed = true;
0760 
0761         if ($remove_cookie) {
0762             self::expireSessionCookie();
0763         }
0764     }
0765 
0766 
0767     /**
0768      * expireSessionCookie() - Sends an expired session id cookie, causing the client to delete the session cookie
0769      *
0770      * @return void
0771      */
0772     public static function expireSessionCookie()
0773     {
0774         if (self::$_unitTestEnabled) {
0775             return;
0776         }
0777 
0778         if (self::$_sessionCookieDeleted) {
0779             return;
0780         }
0781 
0782         self::$_sessionCookieDeleted = true;
0783 
0784         if (isset($_COOKIE[session_name()])) {
0785             $cookie_params = session_get_cookie_params();
0786 
0787             setcookie(
0788                 session_name(),
0789                 false,
0790                 315554400, // strtotime('1980-01-01'),
0791                 $cookie_params['path'],
0792                 $cookie_params['domain'],
0793                 $cookie_params['secure']
0794                 );
0795         }
0796     }
0797 
0798 
0799     /**
0800      * _processValidator() - internal function that is called in the existence of VALID metadata
0801      *
0802      * @throws Zend_Session_Exception
0803      * @return void
0804      */
0805     private static function _processValidators()
0806     {
0807         foreach ($_SESSION['__ZF']['VALID'] as $validator_name => $valid_data) {
0808             if (!class_exists($validator_name)) {
0809                 // require_once 'Zend/Loader.php';
0810                 Zend_Loader::loadClass($validator_name);
0811             }
0812             $validator = new $validator_name;
0813             if ($validator->validate() === false) {
0814                 /** @see Zend_Session_Validator_Exception */
0815                 // require_once 'Zend/Session/Validator/Exception.php';
0816                 throw new Zend_Session_Validator_Exception("This session is not valid according to {$validator_name}.");
0817             }
0818         }
0819     }
0820 
0821 
0822     /**
0823      * namespaceIsset() - check to see if a namespace is set
0824      *
0825      * @param string $namespace
0826      * @return bool
0827      */
0828     public static function namespaceIsset($namespace)
0829     {
0830         return parent::_namespaceIsset($namespace);
0831     }
0832 
0833 
0834     /**
0835      * namespaceUnset() - unset a namespace or a variable within a namespace
0836      *
0837      * @param string $namespace
0838      * @throws Zend_Session_Exception
0839      * @return void
0840      */
0841     public static function namespaceUnset($namespace)
0842     {
0843         parent::_namespaceUnset($namespace);
0844         Zend_Session_Namespace::resetSingleInstance($namespace);
0845     }
0846 
0847 
0848     /**
0849      * namespaceGet() - get all variables in a namespace
0850      * Deprecated: Use getIterator() in Zend_Session_Namespace.
0851      *
0852      * @param string $namespace
0853      * @return array
0854      */
0855     public static function namespaceGet($namespace)
0856     {
0857         return parent::_namespaceGetAll($namespace);
0858     }
0859 
0860 
0861     /**
0862      * getIterator() - return an iteratable object for use in foreach and the like,
0863      * this completes the IteratorAggregate interface
0864      *
0865      * @throws Zend_Session_Exception
0866      * @return ArrayObject
0867      */
0868     public static function getIterator()
0869     {
0870         if (parent::$_readable === false) {
0871             /** @see Zend_Session_Exception */
0872             // require_once 'Zend/Session/Exception.php';
0873             throw new Zend_Session_Exception(parent::_THROW_NOT_READABLE_MSG);
0874         }
0875 
0876         $spaces  = array();
0877         if (isset($_SESSION)) {
0878             $spaces = array_keys($_SESSION);
0879             foreach($spaces as $key => $space) {
0880                 if (!strncmp($space, '__', 2) || !is_array($_SESSION[$space])) {
0881                     unset($spaces[$key]);
0882                 }
0883             }
0884         }
0885 
0886         return new ArrayObject(array_merge($spaces, array_keys(parent::$_expiringData)));
0887     }
0888 
0889 
0890     /**
0891      * isWritable() - returns a boolean indicating if namespaces can write (use setters)
0892      *
0893      * @return bool
0894      */
0895     public static function isWritable()
0896     {
0897         return parent::$_writable;
0898     }
0899 
0900 
0901     /**
0902      * isReadable() - returns a boolean indicating if namespaces can write (use setters)
0903      *
0904      * @return bool
0905      */
0906     public static function isReadable()
0907     {
0908         return parent::$_readable;
0909     }
0910 
0911 }