Namespace.php 16 KB

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