File indexing completed on 2024-12-22 05:37:06

0001 <?php
0002 /**
0003  * Zend Framework
0004  *
0005  * LICENSE
0006  *
0007  * This source file is subject to the new BSD license that is bundled
0008  * with this package in the file LICENSE.txt.
0009  * It is also available through the world-wide-web at this URL:
0010  * http://framework.zend.com/license/new-bsd
0011  * If you did not receive a copy of the license and are unable to
0012  * obtain it through the world-wide-web, please send an email
0013  * to license@zend.com so we can send you a copy immediately.
0014  *
0015  * @category   Zend
0016  * @package    Zend_Session
0017  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0018  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0019  * @version    $Id$
0020  * @since      Preview Release 0.2
0021  */
0022 
0023 
0024 /**
0025  * @see Zend_Session
0026  */
0027 // require_once 'Zend/Session.php';
0028 
0029 
0030 /**
0031  * @see Zend_Session_Abstract
0032  */
0033 // require_once 'Zend/Session/Abstract.php';
0034 
0035 
0036 /**
0037  * Zend_Session_Namespace
0038  *
0039  * @category   Zend
0040  * @package    Zend_Session
0041  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0042  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0043  */
0044 class Zend_Session_Namespace extends Zend_Session_Abstract implements IteratorAggregate
0045 {
0046 
0047     /**
0048      * used as option to constructor to prevent additional instances to the same namespace
0049      */
0050     const SINGLE_INSTANCE = true;
0051 
0052     /**
0053      * Namespace - which namespace this instance of zend-session is saving-to/getting-from
0054      *
0055      * @var string
0056      */
0057     protected $_namespace = "Default";
0058 
0059     /**
0060      * Namespace locking mechanism
0061      *
0062      * @var array
0063      */
0064     protected static $_namespaceLocks = array();
0065 
0066     /**
0067      * Single instance namespace array to ensure data security.
0068      *
0069      * @var array
0070      */
0071     protected static $_singleInstances = array();
0072 
0073     /**
0074      * resetSingleInstance()
0075      *
0076      * @param string $namespaceName
0077      * @return null
0078      */
0079     public static function resetSingleInstance($namespaceName = null)
0080     {
0081         if ($namespaceName != null) {
0082             if (array_key_exists($namespaceName, self::$_singleInstances)) {
0083                 unset(self::$_singleInstances[$namespaceName]);
0084             }
0085             return;
0086         }
0087 
0088         self::$_singleInstances = array();
0089         return;
0090     }
0091 
0092     /**
0093      * __construct() - Returns an instance object bound to a particular, isolated section
0094      * of the session, identified by $namespace name (defaulting to 'Default').
0095      * The optional argument $singleInstance will prevent construction of additional
0096      * instance objects acting as accessors to this $namespace.
0097      *
0098      * @param string $namespace       - programmatic name of the requested namespace
0099      * @param bool $singleInstance    - prevent creation of additional accessor instance objects for this namespace
0100      * @return void
0101      */
0102     public function __construct($namespace = 'Default', $singleInstance = false)
0103     {
0104         if ($namespace === '') {
0105             /**
0106              * @see Zend_Session_Exception
0107              */
0108             // require_once 'Zend/Session/Exception.php';
0109             throw new Zend_Session_Exception('Session namespace must be a non-empty string.');
0110         }
0111 
0112         if ($namespace[0] == "_") {
0113             /**
0114              * @see Zend_Session_Exception
0115              */
0116             // require_once 'Zend/Session/Exception.php';
0117             throw new Zend_Session_Exception('Session namespace must not start with an underscore.');
0118         }
0119 
0120         if (preg_match('#(^[0-9])#i', $namespace[0])) {
0121             /**
0122              * @see Zend_Session_Exception
0123              */
0124             // require_once 'Zend/Session/Exception.php';
0125             throw new Zend_Session_Exception('Session namespace must not start with a number.');
0126         }
0127 
0128         if (isset(self::$_singleInstances[$namespace])) {
0129             /**
0130              * @see Zend_Session_Exception
0131              */
0132             // require_once 'Zend/Session/Exception.php';
0133             throw new Zend_Session_Exception("A session namespace object already exists for this namespace ('$namespace'), and no additional accessors (session namespace objects) for this namespace are permitted.");
0134         }
0135 
0136         if ($singleInstance === true) {
0137             self::$_singleInstances[$namespace] = true;
0138         }
0139 
0140         $this->_namespace = $namespace;
0141 
0142         // Process metadata specific only to this namespace.
0143         Zend_Session::start(true); // attempt auto-start (throws exception if strict option set)
0144 
0145         if (self::$_readable === false) {
0146             /**
0147              * @see Zend_Session_Exception
0148              */
0149             // require_once 'Zend/Session/Exception.php';
0150             throw new Zend_Session_Exception(self::_THROW_NOT_READABLE_MSG);
0151         }
0152 
0153         if (!isset($_SESSION['__ZF'])) {
0154             return; // no further processing needed
0155         }
0156 
0157         // do not allow write access to namespaces, after stop() or writeClose()
0158         if (parent::$_writable === true) {
0159             if (isset($_SESSION['__ZF'][$namespace])) {
0160 
0161                 // Expire Namespace by Namespace Hop (ENNH)
0162                 if (isset($_SESSION['__ZF'][$namespace]['ENNH'])) {
0163                     $_SESSION['__ZF'][$namespace]['ENNH']--;
0164 
0165                     if ($_SESSION['__ZF'][$namespace]['ENNH'] === 0) {
0166                         if (isset($_SESSION[$namespace])) {
0167                             self::$_expiringData[$namespace] = $_SESSION[$namespace];
0168                             unset($_SESSION[$namespace]);
0169                         }
0170                         unset($_SESSION['__ZF'][$namespace]);
0171                     }
0172                 }
0173 
0174                 // Expire Namespace Variables by Namespace Hop (ENVNH)
0175                 if (isset($_SESSION['__ZF'][$namespace]['ENVNH'])) {
0176                     foreach ($_SESSION['__ZF'][$namespace]['ENVNH'] as $variable => $hops) {
0177                         $_SESSION['__ZF'][$namespace]['ENVNH'][$variable]--;
0178 
0179                         if ($_SESSION['__ZF'][$namespace]['ENVNH'][$variable] === 0) {
0180                             if (isset($_SESSION[$namespace][$variable])) {
0181                                 self::$_expiringData[$namespace][$variable] = $_SESSION[$namespace][$variable];
0182                                 unset($_SESSION[$namespace][$variable]);
0183                             }
0184                             unset($_SESSION['__ZF'][$namespace]['ENVNH'][$variable]);
0185                         }
0186                     }
0187                     if(empty($_SESSION['__ZF'][$namespace]['ENVNH'])) {
0188                         unset($_SESSION['__ZF'][$namespace]['ENVNH']);
0189                     }
0190                 }
0191             }
0192 
0193             if (empty($_SESSION['__ZF'][$namespace])) {
0194                 unset($_SESSION['__ZF'][$namespace]);
0195             }
0196 
0197             if (empty($_SESSION['__ZF'])) {
0198                 unset($_SESSION['__ZF']);
0199             }
0200         }
0201     }
0202 
0203 
0204     /**
0205      * getIterator() - return an iteratable object for use in foreach and the like,
0206      * this completes the IteratorAggregate interface
0207      *
0208      * @return ArrayObject - iteratable container of the namespace contents
0209      */
0210     public function getIterator()
0211     {
0212         return new ArrayObject(parent::_namespaceGetAll($this->_namespace));
0213     }
0214 
0215 
0216     /**
0217      * lock() - mark a session/namespace as readonly
0218      *
0219      * @return void
0220      */
0221     public function lock()
0222     {
0223         self::$_namespaceLocks[$this->_namespace] = true;
0224     }
0225 
0226 
0227     /**
0228      * unlock() - unmark a session/namespace to enable read & write
0229      *
0230      * @return void
0231      */
0232     public function unlock()
0233     {
0234         unset(self::$_namespaceLocks[$this->_namespace]);
0235     }
0236 
0237 
0238     /**
0239      * unlockAll() - unmark all session/namespaces to enable read & write
0240      *
0241      * @return void
0242      */
0243     public static function unlockAll()
0244     {
0245         self::$_namespaceLocks = array();
0246     }
0247 
0248 
0249     /**
0250      * isLocked() - return lock status, true if, and only if, read-only
0251      *
0252      * @return bool
0253      */
0254     public function isLocked()
0255     {
0256         return isset(self::$_namespaceLocks[$this->_namespace]);
0257     }
0258 
0259 
0260     /**
0261      * unsetAll() - unset all variables in this namespace
0262      *
0263      * @return true
0264      */
0265     public function unsetAll()
0266     {
0267         return parent::_namespaceUnset($this->_namespace);
0268     }
0269 
0270 
0271     /**
0272      * __get() - method to get a variable in this object's current namespace
0273      *
0274      * @param string $name - programmatic name of a key, in a <key,value> pair in the current namespace
0275      * @return mixed
0276      */
0277     public function & __get($name)
0278     {
0279         if ($name === '') {
0280             /**
0281              * @see Zend_Session_Exception
0282              */
0283             // require_once 'Zend/Session/Exception.php';
0284             throw new Zend_Session_Exception("The '$name' key must be a non-empty string");
0285         }
0286 
0287         return parent::_namespaceGet($this->_namespace, $name);
0288     }
0289 
0290 
0291     /**
0292      * __set() - method to set a variable/value in this object's namespace
0293      *
0294      * @param string $name - programmatic name of a key, in a <key,value> pair in the current namespace
0295      * @param mixed $value - value in the <key,value> pair to assign to the $name key
0296      * @throws Zend_Session_Exception
0297      * @return true
0298      */
0299     public function __set($name, $value)
0300     {
0301         if (isset(self::$_namespaceLocks[$this->_namespace])) {
0302             /**
0303              * @see Zend_Session_Exception
0304              */
0305             // require_once 'Zend/Session/Exception.php';
0306             throw new Zend_Session_Exception('This session/namespace has been marked as read-only.');
0307         }
0308 
0309         if ($name === '') {
0310             /**
0311              * @see Zend_Session_Exception
0312              */
0313             // require_once 'Zend/Session/Exception.php';
0314             throw new Zend_Session_Exception("The '$name' key must be a non-empty string");
0315         }
0316 
0317         if (parent::$_writable === false) {
0318             /**
0319              * @see Zend_Session_Exception
0320              */
0321             // require_once 'Zend/Session/Exception.php';
0322             throw new Zend_Session_Exception(parent::_THROW_NOT_WRITABLE_MSG);
0323         }
0324 
0325         $name = (string) $name;
0326 
0327         $_SESSION[$this->_namespace][$name] = $value;
0328     }
0329 
0330 
0331     /**
0332      * apply() - enables applying user-selected function, such as array_merge() to the namespace
0333      * Parameters following the $callback argument are passed to the callback function.
0334      * Caveat: ignores members expiring now.
0335      *
0336      * Example:
0337      *   $namespace->apply('array_merge', array('tree' => 'apple', 'fruit' => 'peach'), array('flower' => 'rose'));
0338      *   $namespace->apply('count');
0339      *
0340      * @param string|array $callback - callback function
0341      */
0342     public function apply($callback)
0343     {
0344         $arg_list = func_get_args();
0345         $arg_list[0] = $_SESSION[$this->_namespace];
0346         return call_user_func_array($callback, $arg_list);
0347     }
0348 
0349 
0350     /**
0351      * applySet() - enables applying user-selected function, and sets entire namespace to the result
0352      * Result of $callback must be an array.
0353      * Parameters following the $callback argument are passed to the callback function.
0354      * Caveat: ignores members expiring now.
0355      *
0356      * Example:
0357      *   $namespace->applySet('array_merge', array('tree' => 'apple', 'fruit' => 'peach'), array('flower' => 'rose'));
0358      *
0359      * @param string|array $callback - callback function
0360      */
0361     public function applySet($callback)
0362     {
0363         $arg_list = func_get_args();
0364         $arg_list[0] = $_SESSION[$this->_namespace];
0365         $result = call_user_func_array($callback, $arg_list);
0366         if (!is_array($result)) {
0367             /**
0368              * @see Zend_Session_Exception
0369              */
0370             // require_once 'Zend/Session/Exception.php';
0371             throw new Zend_Session_Exception('Result must be an array. Got: ' . gettype($result));
0372         }
0373         $_SESSION[$this->_namespace] = $result;
0374         return $result;
0375     }
0376 
0377 
0378     /**
0379      * __isset() - determine if a variable in this object's namespace is set
0380      *
0381      * @param string $name - programmatic name of a key, in a <key,value> pair in the current namespace
0382      * @return bool
0383      */
0384     public function __isset($name)
0385     {
0386         if ($name === '') {
0387             /**
0388              * @see Zend_Session_Exception
0389              */
0390             // require_once 'Zend/Session/Exception.php';
0391             throw new Zend_Session_Exception("The '$name' key must be a non-empty string");
0392         }
0393 
0394         return parent::_namespaceIsset($this->_namespace, $name);
0395     }
0396 
0397 
0398     /**
0399      * __unset() - unset a variable in this object's namespace.
0400      *
0401      * @param string $name - programmatic name of a key, in a <key,value> pair in the current namespace
0402      * @return true
0403      */
0404     public function __unset($name)
0405     {
0406         if ($name === '') {
0407             /**
0408              * @see Zend_Session_Exception
0409              */
0410             // require_once 'Zend/Session/Exception.php';
0411             throw new Zend_Session_Exception("The '$name' key must be a non-empty string");
0412         }
0413 
0414         return parent::_namespaceUnset($this->_namespace, $name);
0415     }
0416 
0417 
0418     /**
0419      * setExpirationSeconds() - expire the namespace, or specific variables after a specified
0420      * number of seconds
0421      *
0422      * @param int $seconds     - expires in this many seconds
0423      * @param mixed $variables - OPTIONAL list of variables to expire (defaults to all)
0424      * @throws Zend_Session_Exception
0425      * @return void
0426      */
0427     public function setExpirationSeconds($seconds, $variables = null)
0428     {
0429         if (parent::$_writable === false) {
0430             /**
0431              * @see Zend_Session_Exception
0432              */
0433             // require_once 'Zend/Session/Exception.php';
0434             throw new Zend_Session_Exception(parent::_THROW_NOT_WRITABLE_MSG);
0435         }
0436 
0437         if ($seconds <= 0) {
0438             /**
0439              * @see Zend_Session_Exception
0440              */
0441             // require_once 'Zend/Session/Exception.php';
0442             throw new Zend_Session_Exception('Seconds must be positive.');
0443         }
0444 
0445         if ($variables === null) {
0446 
0447             // apply expiration to entire namespace
0448             $_SESSION['__ZF'][$this->_namespace]['ENT'] = time() + $seconds;
0449 
0450         } else {
0451 
0452             if (is_string($variables)) {
0453                 $variables = array($variables);
0454             }
0455 
0456             foreach ($variables as $variable) {
0457                 if (!empty($variable)) {
0458                     $_SESSION['__ZF'][$this->_namespace]['ENVT'][$variable] = time() + $seconds;
0459                 }
0460             }
0461         }
0462     }
0463 
0464 
0465     /**
0466      * setExpirationHops() - expire the namespace, or specific variables after a specified
0467      * number of page hops
0468      *
0469      * @param int $hops        - how many "hops" (number of subsequent requests) before expiring
0470      * @param mixed $variables - OPTIONAL list of variables to expire (defaults to all)
0471      * @param boolean $hopCountOnUsageOnly - OPTIONAL if set, only count a hop/request if this namespace is used
0472      * @throws Zend_Session_Exception
0473      * @return void
0474      */
0475     public function setExpirationHops($hops, $variables = null, $hopCountOnUsageOnly = false)
0476     {
0477         if (parent::$_writable === false) {
0478             /**
0479              * @see Zend_Session_Exception
0480              */
0481             // require_once 'Zend/Session/Exception.php';
0482             throw new Zend_Session_Exception(parent::_THROW_NOT_WRITABLE_MSG);
0483         }
0484 
0485         if ($hops <= 0) {
0486             /**
0487              * @see Zend_Session_Exception
0488              */
0489             // require_once 'Zend/Session/Exception.php';
0490             throw new Zend_Session_Exception('Hops must be positive number.');
0491         }
0492 
0493         if ($variables === null) {
0494 
0495             // apply expiration to entire namespace
0496             if ($hopCountOnUsageOnly === false) {
0497                 $_SESSION['__ZF'][$this->_namespace]['ENGH'] = $hops;
0498             } else {
0499                 $_SESSION['__ZF'][$this->_namespace]['ENNH'] = $hops;
0500             }
0501 
0502         } else {
0503 
0504             if (is_string($variables)) {
0505                 $variables = array($variables);
0506             }
0507 
0508             foreach ($variables as $variable) {
0509                 if (!empty($variable)) {
0510                     if ($hopCountOnUsageOnly === false) {
0511                         $_SESSION['__ZF'][$this->_namespace]['ENVGH'][$variable] = $hops;
0512                     } else {
0513                         $_SESSION['__ZF'][$this->_namespace]['ENVNH'][$variable] = $hops;
0514                     }
0515                 }
0516             }
0517         }
0518     }
0519 
0520     /**
0521      * Returns the namespace name
0522      *
0523      * @return string
0524      */
0525     public function getNamespace()
0526     {
0527         return $this->_namespace;
0528     }
0529 }