Namespace.php 16 KB

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