1<?php 2/** 3 * Zend Framework 4 * 5 * LICENSE 6 * 7 * This source file is subject to the new BSD license that is bundled 8 * with this package in the file LICENSE.txt. 9 * It is also available through the world-wide-web at this URL: 10 * http://framework.zend.com/license/new-bsd 11 * If you did not receive a copy of the license and are unable to 12 * obtain it through the world-wide-web, please send an email 13 * to license@zend.com so we can send you a copy immediately. 14 * 15 * @category Zend 16 * @package Zend_Session 17 * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com) 18 * @license http://framework.zend.com/license/new-bsd New BSD License 19 * @version $Id: Namespace.php 23775 2011-03-01 17:25:24Z ralph $ 20 * @since Preview Release 0.2 21 */ 22 23 24/** 25 * @see Zend_Session 26 */ 27// require_once 'Zend/Session.php'; 28 29 30/** 31 * @see Zend_Session_Abstract 32 */ 33// require_once 'Zend/Session/Abstract.php'; 34 35 36/** 37 * Zend_Session_Namespace 38 * 39 * @category Zend 40 * @package Zend_Session 41 * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com) 42 * @license http://framework.zend.com/license/new-bsd New BSD License 43 */ 44class Zend_Session_Namespace extends Zend_Session_Abstract implements IteratorAggregate 45{ 46 47 /** 48 * used as option to constructor to prevent additional instances to the same namespace 49 */ 50 const SINGLE_INSTANCE = true; 51 52 /** 53 * Namespace - which namespace this instance of zend-session is saving-to/getting-from 54 * 55 * @var string 56 */ 57 protected $_namespace = "Default"; 58 59 /** 60 * Namespace locking mechanism 61 * 62 * @var array 63 */ 64 protected static $_namespaceLocks = array(); 65 66 /** 67 * Single instance namespace array to ensure data security. 68 * 69 * @var array 70 */ 71 protected static $_singleInstances = array(); 72 73 /** 74 * resetSingleInstance() 75 * 76 * @param string $namespaceName 77 * @return null 78 */ 79 public static function resetSingleInstance($namespaceName = null) 80 { 81 if ($namespaceName != null) { 82 if (array_key_exists($namespaceName, self::$_singleInstances)) { 83 unset(self::$_singleInstances[$namespaceName]); 84 } 85 return; 86 } 87 88 self::$_singleInstances = array(); 89 return; 90 } 91 92 /** 93 * __construct() - Returns an instance object bound to a particular, isolated section 94 * of the session, identified by $namespace name (defaulting to 'Default'). 95 * The optional argument $singleInstance will prevent construction of additional 96 * instance objects acting as accessors to this $namespace. 97 * 98 * @param string $namespace - programmatic name of the requested namespace 99 * @param bool $singleInstance - prevent creation of additional accessor instance objects for this namespace 100 * @return void 101 */ 102 public function __construct($namespace = 'Default', $singleInstance = false) 103 { 104 if ($namespace === '') { 105 /** 106 * @see Zend_Session_Exception 107 */ 108 // require_once 'Zend/Session/Exception.php'; 109 throw new Zend_Session_Exception('Session namespace must be a non-empty string.'); 110 } 111 112 if ($namespace[0] == "_") { 113 /** 114 * @see Zend_Session_Exception 115 */ 116 // require_once 'Zend/Session/Exception.php'; 117 throw new Zend_Session_Exception('Session namespace must not start with an underscore.'); 118 } 119 120 if (preg_match('#(^[0-9])#i', $namespace[0])) { 121 /** 122 * @see Zend_Session_Exception 123 */ 124 // require_once 'Zend/Session/Exception.php'; 125 throw new Zend_Session_Exception('Session namespace must not start with a number.'); 126 } 127 128 if (isset(self::$_singleInstances[$namespace])) { 129 /** 130 * @see Zend_Session_Exception 131 */ 132 // require_once 'Zend/Session/Exception.php'; 133 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."); 134 } 135 136 if ($singleInstance === true) { 137 self::$_singleInstances[$namespace] = true; 138 } 139 140 $this->_namespace = $namespace; 141 142 // Process metadata specific only to this namespace. 143 Zend_Session::start(true); // attempt auto-start (throws exception if strict option set) 144 145 if (self::$_readable === false) { 146 /** 147 * @see Zend_Session_Exception 148 */ 149 // require_once 'Zend/Session/Exception.php'; 150 throw new Zend_Session_Exception(self::_THROW_NOT_READABLE_MSG); 151 } 152 153 if (!isset($_SESSION['__ZF'])) { 154 return; // no further processing needed 155 } 156 157 // do not allow write access to namespaces, after stop() or writeClose() 158 if (parent::$_writable === true) { 159 if (isset($_SESSION['__ZF'][$namespace])) { 160 161 // Expire Namespace by Namespace Hop (ENNH) 162 if (isset($_SESSION['__ZF'][$namespace]['ENNH'])) { 163 $_SESSION['__ZF'][$namespace]['ENNH']--; 164 165 if ($_SESSION['__ZF'][$namespace]['ENNH'] === 0) { 166 if (isset($_SESSION[$namespace])) { 167 self::$_expiringData[$namespace] = $_SESSION[$namespace]; 168 unset($_SESSION[$namespace]); 169 } 170 unset($_SESSION['__ZF'][$namespace]); 171 } 172 } 173 174 // Expire Namespace Variables by Namespace Hop (ENVNH) 175 if (isset($_SESSION['__ZF'][$namespace]['ENVNH'])) { 176 foreach ($_SESSION['__ZF'][$namespace]['ENVNH'] as $variable => $hops) { 177 $_SESSION['__ZF'][$namespace]['ENVNH'][$variable]--; 178 179 if ($_SESSION['__ZF'][$namespace]['ENVNH'][$variable] === 0) { 180 if (isset($_SESSION[$namespace][$variable])) { 181 self::$_expiringData[$namespace][$variable] = $_SESSION[$namespace][$variable]; 182 unset($_SESSION[$namespace][$variable]); 183 } 184 unset($_SESSION['__ZF'][$namespace]['ENVNH'][$variable]); 185 } 186 } 187 if(empty($_SESSION['__ZF'][$namespace]['ENVNH'])) { 188 unset($_SESSION['__ZF'][$namespace]['ENVNH']); 189 } 190 } 191 } 192 193 if (empty($_SESSION['__ZF'][$namespace])) { 194 unset($_SESSION['__ZF'][$namespace]); 195 } 196 197 if (empty($_SESSION['__ZF'])) { 198 unset($_SESSION['__ZF']); 199 } 200 } 201 } 202 203 204 /** 205 * getIterator() - return an iteratable object for use in foreach and the like, 206 * this completes the IteratorAggregate interface 207 * 208 * @return ArrayObject - iteratable container of the namespace contents 209 */ 210 public function getIterator(): ArrayObject 211 { 212 return new ArrayObject(parent::_namespaceGetAll($this->_namespace)); 213 } 214 215 216 /** 217 * lock() - mark a session/namespace as readonly 218 * 219 * @return void 220 */ 221 public function lock() 222 { 223 self::$_namespaceLocks[$this->_namespace] = true; 224 } 225 226 227 /** 228 * unlock() - unmark a session/namespace to enable read & write 229 * 230 * @return void 231 */ 232 public function unlock() 233 { 234 unset(self::$_namespaceLocks[$this->_namespace]); 235 } 236 237 238 /** 239 * unlockAll() - unmark all session/namespaces to enable read & write 240 * 241 * @return void 242 */ 243 public static function unlockAll() 244 { 245 self::$_namespaceLocks = array(); 246 } 247 248 249 /** 250 * isLocked() - return lock status, true if, and only if, read-only 251 * 252 * @return bool 253 */ 254 public function isLocked() 255 { 256 return isset(self::$_namespaceLocks[$this->_namespace]); 257 } 258 259 260 /** 261 * unsetAll() - unset all variables in this namespace 262 * 263 * @return true 264 */ 265 public function unsetAll() 266 { 267 return parent::_namespaceUnset($this->_namespace); 268 } 269 270 271 /** 272 * __get() - method to get a variable in this object's current namespace 273 * 274 * @param string $name - programmatic name of a key, in a <key,value> pair in the current namespace 275 * @return mixed 276 */ 277 public function & __get($name) 278 { 279 if ($name === '') { 280 /** 281 * @see Zend_Session_Exception 282 */ 283 // require_once 'Zend/Session/Exception.php'; 284 throw new Zend_Session_Exception("The '$name' key must be a non-empty string"); 285 } 286 287 return parent::_namespaceGet($this->_namespace, $name); 288 } 289 290 291 /** 292 * __set() - method to set a variable/value in this object's namespace 293 * 294 * @param string $name - programmatic name of a key, in a <key,value> pair in the current namespace 295 * @param mixed $value - value in the <key,value> pair to assign to the $name key 296 * @throws Zend_Session_Exception 297 * @return true 298 */ 299 public function __set($name, $value) 300 { 301 if (isset(self::$_namespaceLocks[$this->_namespace])) { 302 /** 303 * @see Zend_Session_Exception 304 */ 305 // require_once 'Zend/Session/Exception.php'; 306 throw new Zend_Session_Exception('This session/namespace has been marked as read-only.'); 307 } 308 309 if ($name === '') { 310 /** 311 * @see Zend_Session_Exception 312 */ 313 // require_once 'Zend/Session/Exception.php'; 314 throw new Zend_Session_Exception("The '$name' key must be a non-empty string"); 315 } 316 317 if (parent::$_writable === false) { 318 /** 319 * @see Zend_Session_Exception 320 */ 321 // require_once 'Zend/Session/Exception.php'; 322 throw new Zend_Session_Exception(parent::_THROW_NOT_WRITABLE_MSG); 323 } 324 325 $name = (string) $name; 326 327 $_SESSION[$this->_namespace][$name] = $value; 328 } 329 330 331 /** 332 * apply() - enables applying user-selected function, such as array_merge() to the namespace 333 * Parameters following the $callback argument are passed to the callback function. 334 * Caveat: ignores members expiring now. 335 * 336 * Example: 337 * $namespace->apply('array_merge', array('tree' => 'apple', 'fruit' => 'peach'), array('flower' => 'rose')); 338 * $namespace->apply('count'); 339 * 340 * @param string|array $callback - callback function 341 */ 342 public function apply($callback) 343 { 344 $arg_list = func_get_args(); 345 $arg_list[0] = $_SESSION[$this->_namespace]; 346 return call_user_func_array($callback, $arg_list); 347 } 348 349 350 /** 351 * applySet() - enables applying user-selected function, and sets entire namespace to the result 352 * Result of $callback must be an array. 353 * Parameters following the $callback argument are passed to the callback function. 354 * Caveat: ignores members expiring now. 355 * 356 * Example: 357 * $namespace->applySet('array_merge', array('tree' => 'apple', 'fruit' => 'peach'), array('flower' => 'rose')); 358 * 359 * @param string|array $callback - callback function 360 */ 361 public function applySet($callback) 362 { 363 $arg_list = func_get_args(); 364 $arg_list[0] = $_SESSION[$this->_namespace]; 365 $result = call_user_func_array($callback, $arg_list); 366 if (!is_array($result)) { 367 /** 368 * @see Zend_Session_Exception 369 */ 370 // require_once 'Zend/Session/Exception.php'; 371 throw new Zend_Session_Exception('Result must be an array. Got: ' . gettype($result)); 372 } 373 $_SESSION[$this->_namespace] = $result; 374 return $result; 375 } 376 377 378 /** 379 * __isset() - determine if a variable in this object's namespace is set 380 * 381 * @param string $name - programmatic name of a key, in a <key,value> pair in the current namespace 382 * @return bool 383 */ 384 public function __isset($name) 385 { 386 if ($name === '') { 387 /** 388 * @see Zend_Session_Exception 389 */ 390 // require_once 'Zend/Session/Exception.php'; 391 throw new Zend_Session_Exception("The '$name' key must be a non-empty string"); 392 } 393 394 return parent::_namespaceIsset($this->_namespace, $name); 395 } 396 397 398 /** 399 * __unset() - unset a variable in this object's namespace. 400 * 401 * @param string $name - programmatic name of a key, in a <key,value> pair in the current namespace 402 * @return true 403 */ 404 public function __unset($name) 405 { 406 if ($name === '') { 407 /** 408 * @see Zend_Session_Exception 409 */ 410 // require_once 'Zend/Session/Exception.php'; 411 throw new Zend_Session_Exception("The '$name' key must be a non-empty string"); 412 } 413 414 return parent::_namespaceUnset($this->_namespace, $name); 415 } 416 417 418 /** 419 * setExpirationSeconds() - expire the namespace, or specific variables after a specified 420 * number of seconds 421 * 422 * @param int $seconds - expires in this many seconds 423 * @param mixed $variables - OPTIONAL list of variables to expire (defaults to all) 424 * @throws Zend_Session_Exception 425 * @return void 426 */ 427 public function setExpirationSeconds($seconds, $variables = null) 428 { 429 if (parent::$_writable === false) { 430 /** 431 * @see Zend_Session_Exception 432 */ 433 // require_once 'Zend/Session/Exception.php'; 434 throw new Zend_Session_Exception(parent::_THROW_NOT_WRITABLE_MSG); 435 } 436 437 if ($seconds <= 0) { 438 /** 439 * @see Zend_Session_Exception 440 */ 441 // require_once 'Zend/Session/Exception.php'; 442 throw new Zend_Session_Exception('Seconds must be positive.'); 443 } 444 445 if ($variables === null) { 446 447 // apply expiration to entire namespace 448 $_SESSION['__ZF'][$this->_namespace]['ENT'] = time() + $seconds; 449 450 } else { 451 452 if (is_string($variables)) { 453 $variables = array($variables); 454 } 455 456 foreach ($variables as $variable) { 457 if (!empty($variable)) { 458 $_SESSION['__ZF'][$this->_namespace]['ENVT'][$variable] = time() + $seconds; 459 } 460 } 461 } 462 } 463 464 465 /** 466 * setExpirationHops() - expire the namespace, or specific variables after a specified 467 * number of page hops 468 * 469 * @param int $hops - how many "hops" (number of subsequent requests) before expiring 470 * @param mixed $variables - OPTIONAL list of variables to expire (defaults to all) 471 * @param boolean $hopCountOnUsageOnly - OPTIONAL if set, only count a hop/request if this namespace is used 472 * @throws Zend_Session_Exception 473 * @return void 474 */ 475 public function setExpirationHops($hops, $variables = null, $hopCountOnUsageOnly = false) 476 { 477 if (parent::$_writable === false) { 478 /** 479 * @see Zend_Session_Exception 480 */ 481 // require_once 'Zend/Session/Exception.php'; 482 throw new Zend_Session_Exception(parent::_THROW_NOT_WRITABLE_MSG); 483 } 484 485 if ($hops <= 0) { 486 /** 487 * @see Zend_Session_Exception 488 */ 489 // require_once 'Zend/Session/Exception.php'; 490 throw new Zend_Session_Exception('Hops must be positive number.'); 491 } 492 493 if ($variables === null) { 494 495 // apply expiration to entire namespace 496 if ($hopCountOnUsageOnly === false) { 497 $_SESSION['__ZF'][$this->_namespace]['ENGH'] = $hops; 498 } else { 499 $_SESSION['__ZF'][$this->_namespace]['ENNH'] = $hops; 500 } 501 502 } else { 503 504 if (is_string($variables)) { 505 $variables = array($variables); 506 } 507 508 foreach ($variables as $variable) { 509 if (!empty($variable)) { 510 if ($hopCountOnUsageOnly === false) { 511 $_SESSION['__ZF'][$this->_namespace]['ENVGH'][$variable] = $hops; 512 } else { 513 $_SESSION['__ZF'][$this->_namespace]['ENVNH'][$variable] = $hops; 514 } 515 } 516 } 517 } 518 } 519 520 /** 521 * Returns the namespace name 522 * 523 * @return string 524 */ 525 public function getNamespace() 526 { 527 return $this->_namespace; 528 } 529} 530