Session.php 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906
  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-2012 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_Abstract
  24. */
  25. require_once 'Zend/Session/Abstract.php';
  26. /**
  27. * @see Zend_Session_Namespace
  28. */
  29. require_once 'Zend/Session/Namespace.php';
  30. /**
  31. * @see Zend_Session_SaveHandler_Interface
  32. */
  33. require_once 'Zend/Session/SaveHandler/Interface.php';
  34. /**
  35. * Zend_Session
  36. *
  37. * @category Zend
  38. * @package Zend_Session
  39. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  40. * @license http://framework.zend.com/license/new-bsd New BSD License
  41. */
  42. class Zend_Session extends Zend_Session_Abstract
  43. {
  44. /**
  45. * Whether or not Zend_Session is being used with unit tests
  46. *
  47. * @internal
  48. * @var bool
  49. */
  50. public static $_unitTestEnabled = false;
  51. /**
  52. * $_throwStartupException
  53. *
  54. * @var bool|bitset This could also be a combiniation of error codes to catch
  55. */
  56. protected static $_throwStartupExceptions = true;
  57. /**
  58. * Check whether or not the session was started
  59. *
  60. * @var bool
  61. */
  62. private static $_sessionStarted = false;
  63. /**
  64. * Whether or not the session id has been regenerated this request.
  65. *
  66. * Id regeneration state
  67. * <0 - regenerate requested when session is started
  68. * 0 - do nothing
  69. * >0 - already called session_regenerate_id()
  70. *
  71. * @var int
  72. */
  73. private static $_regenerateIdState = 0;
  74. /**
  75. * Private list of php's ini values for ext/session
  76. * null values will default to the php.ini value, otherwise
  77. * the value below will overwrite the default ini value, unless
  78. * the user has set an option explicity with setOptions()
  79. *
  80. * @var array
  81. */
  82. private static $_defaultOptions = array(
  83. 'save_path' => null,
  84. 'name' => null, /* this should be set to a unique value for each application */
  85. 'save_handler' => null,
  86. //'auto_start' => null, /* intentionally excluded (see manual) */
  87. 'gc_probability' => null,
  88. 'gc_divisor' => null,
  89. 'gc_maxlifetime' => null,
  90. 'serialize_handler' => null,
  91. 'cookie_lifetime' => null,
  92. 'cookie_path' => null,
  93. 'cookie_domain' => null,
  94. 'cookie_secure' => null,
  95. 'cookie_httponly' => null,
  96. 'use_cookies' => null,
  97. 'use_only_cookies' => 'on',
  98. 'referer_check' => null,
  99. 'entropy_file' => null,
  100. 'entropy_length' => null,
  101. 'cache_limiter' => null,
  102. 'cache_expire' => null,
  103. 'use_trans_sid' => null,
  104. 'bug_compat_42' => null,
  105. 'bug_compat_warn' => null,
  106. 'hash_function' => null,
  107. 'hash_bits_per_character' => null
  108. );
  109. /**
  110. * List of options pertaining to Zend_Session that can be set by developers
  111. * using Zend_Session::setOptions(). This list intentionally duplicates
  112. * the individual declaration of static "class" variables by the same names.
  113. *
  114. * @var array
  115. */
  116. private static $_localOptions = array(
  117. 'strict' => '_strict',
  118. 'remember_me_seconds' => '_rememberMeSeconds',
  119. 'throw_startup_exceptions' => '_throwStartupExceptions'
  120. );
  121. /**
  122. * Whether or not write close has been performed.
  123. *
  124. * @var bool
  125. */
  126. private static $_writeClosed = false;
  127. /**
  128. * Whether or not session id cookie has been deleted
  129. *
  130. * @var bool
  131. */
  132. private static $_sessionCookieDeleted = false;
  133. /**
  134. * Whether or not session has been destroyed via session_destroy()
  135. *
  136. * @var bool
  137. */
  138. private static $_destroyed = false;
  139. /**
  140. * Whether or not session must be initiated before usage
  141. *
  142. * @var bool
  143. */
  144. private static $_strict = false;
  145. /**
  146. * Default number of seconds the session will be remembered for when asked to be remembered
  147. *
  148. * @var int
  149. */
  150. private static $_rememberMeSeconds = 1209600; // 2 weeks
  151. /**
  152. * Whether the default options listed in Zend_Session::$_localOptions have been set
  153. *
  154. * @var bool
  155. */
  156. private static $_defaultOptionsSet = false;
  157. /**
  158. * A reference to the set session save handler
  159. *
  160. * @var Zend_Session_SaveHandler_Interface
  161. */
  162. private static $_saveHandler = null;
  163. /**
  164. * Constructor overriding - make sure that a developer cannot instantiate
  165. */
  166. protected function __construct()
  167. {
  168. }
  169. /**
  170. * setOptions - set both the class specified
  171. *
  172. * @param array $userOptions - pass-by-keyword style array of <option name, option value> pairs
  173. * @throws Zend_Session_Exception
  174. * @return void
  175. */
  176. public static function setOptions(array $userOptions = array())
  177. {
  178. // set default options on first run only (before applying user settings)
  179. if (!self::$_defaultOptionsSet) {
  180. foreach (self::$_defaultOptions as $defaultOptionName => $defaultOptionValue) {
  181. if (isset(self::$_defaultOptions[$defaultOptionName])) {
  182. ini_set("session.$defaultOptionName", $defaultOptionValue);
  183. }
  184. }
  185. self::$_defaultOptionsSet = true;
  186. }
  187. // set the options the user has requested to set
  188. foreach ($userOptions as $userOptionName => $userOptionValue) {
  189. $userOptionName = strtolower($userOptionName);
  190. // set the ini based values
  191. if (array_key_exists($userOptionName, self::$_defaultOptions)) {
  192. ini_set("session.$userOptionName", $userOptionValue);
  193. }
  194. elseif (isset(self::$_localOptions[$userOptionName])) {
  195. self::${self::$_localOptions[$userOptionName]} = $userOptionValue;
  196. }
  197. else {
  198. /** @see Zend_Session_Exception */
  199. require_once 'Zend/Session/Exception.php';
  200. throw new Zend_Session_Exception("Unknown option: $userOptionName = $userOptionValue");
  201. }
  202. }
  203. }
  204. /**
  205. * getOptions()
  206. *
  207. * @param string $optionName OPTIONAL
  208. * @return array|string
  209. */
  210. public static function getOptions($optionName = null)
  211. {
  212. $options = array();
  213. foreach (ini_get_all('session') as $sysOptionName => $sysOptionValues) {
  214. $options[substr($sysOptionName, 8)] = $sysOptionValues['local_value'];
  215. }
  216. foreach (self::$_localOptions as $localOptionName => $localOptionMemberName) {
  217. $options[$localOptionName] = self::${$localOptionMemberName};
  218. }
  219. if ($optionName) {
  220. if (array_key_exists($optionName, $options)) {
  221. return $options[$optionName];
  222. }
  223. return null;
  224. }
  225. return $options;
  226. }
  227. /**
  228. * setSaveHandler() - Session Save Handler assignment
  229. *
  230. * @param Zend_Session_SaveHandler_Interface $interface
  231. * @return void
  232. */
  233. public static function setSaveHandler(Zend_Session_SaveHandler_Interface $saveHandler)
  234. {
  235. self::$_saveHandler = $saveHandler;
  236. if (self::$_unitTestEnabled) {
  237. return;
  238. }
  239. session_set_save_handler(
  240. array(&$saveHandler, 'open'),
  241. array(&$saveHandler, 'close'),
  242. array(&$saveHandler, 'read'),
  243. array(&$saveHandler, 'write'),
  244. array(&$saveHandler, 'destroy'),
  245. array(&$saveHandler, 'gc')
  246. );
  247. }
  248. /**
  249. * getSaveHandler() - Get the session Save Handler
  250. *
  251. * @return Zend_Session_SaveHandler_Interface
  252. */
  253. public static function getSaveHandler()
  254. {
  255. return self::$_saveHandler;
  256. }
  257. /**
  258. * regenerateId() - Regenerate the session id. Best practice is to call this after
  259. * session is started. If called prior to session starting, session id will be regenerated
  260. * at start time.
  261. *
  262. * @throws Zend_Session_Exception
  263. * @return void
  264. */
  265. public static function regenerateId()
  266. {
  267. if (!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {
  268. /** @see Zend_Session_Exception */
  269. require_once 'Zend/Session/Exception.php';
  270. throw new Zend_Session_Exception("You must call " . __CLASS__ . '::' . __FUNCTION__ .
  271. "() before any output has been sent to the browser; output started in {$filename}/{$linenum}");
  272. }
  273. if ( !self::$_sessionStarted ) {
  274. self::$_regenerateIdState = -1;
  275. } else {
  276. if (!self::$_unitTestEnabled) {
  277. session_regenerate_id(true);
  278. }
  279. self::$_regenerateIdState = 1;
  280. }
  281. }
  282. /**
  283. * rememberMe() - Write a persistent cookie that expires after a number of seconds in the future. If no number of
  284. * seconds is specified, then this defaults to self::$_rememberMeSeconds. Due to clock errors on end users' systems,
  285. * large values are recommended to avoid undesirable expiration of session cookies.
  286. *
  287. * @param int $seconds OPTIONAL specifies TTL for cookie in seconds from present time
  288. * @return void
  289. */
  290. public static function rememberMe($seconds = null)
  291. {
  292. $seconds = (int) $seconds;
  293. $seconds = ($seconds > 0) ? $seconds : self::$_rememberMeSeconds;
  294. self::rememberUntil($seconds);
  295. }
  296. /**
  297. * forgetMe() - Write a volatile session cookie, removing any persistent cookie that may have existed. The session
  298. * would end upon, for example, termination of a web browser program.
  299. *
  300. * @return void
  301. */
  302. public static function forgetMe()
  303. {
  304. self::rememberUntil(0);
  305. }
  306. /**
  307. * rememberUntil() - This method does the work of changing the state of the session cookie and making
  308. * sure that it gets resent to the browser via regenerateId()
  309. *
  310. * @param int $seconds
  311. * @return void
  312. */
  313. public static function rememberUntil($seconds = 0)
  314. {
  315. if (self::$_unitTestEnabled) {
  316. self::regenerateId();
  317. return;
  318. }
  319. $cookieParams = session_get_cookie_params();
  320. session_set_cookie_params(
  321. $seconds,
  322. $cookieParams['path'],
  323. $cookieParams['domain'],
  324. $cookieParams['secure']
  325. );
  326. // normally "rememberMe()" represents a security context change, so should use new session id
  327. self::regenerateId();
  328. }
  329. /**
  330. * sessionExists() - whether or not a session exists for the current request
  331. *
  332. * @return bool
  333. */
  334. public static function sessionExists()
  335. {
  336. if ((bool)ini_get('session.use_cookies') == true && isset($_COOKIE[session_name()])) {
  337. return true;
  338. } elseif ((bool)ini_get('session.use_only_cookies') == false && isset($_REQUEST[session_name()])) {
  339. return true;
  340. } elseif (self::$_unitTestEnabled) {
  341. return true;
  342. }
  343. return false;
  344. }
  345. /**
  346. * Whether or not session has been destroyed via session_destroy()
  347. *
  348. * @return bool
  349. */
  350. public static function isDestroyed()
  351. {
  352. return self::$_destroyed;
  353. }
  354. /**
  355. * start() - Start the session.
  356. *
  357. * @param bool|array $options OPTIONAL Either user supplied options, or flag indicating if start initiated automatically
  358. * @throws Zend_Session_Exception
  359. * @return void
  360. */
  361. public static function start($options = false)
  362. {
  363. // Check to see if we've been passed an invalid session ID
  364. if ( self::getId() && !self::_checkId(self::getId()) ) {
  365. // Generate a valid, temporary replacement
  366. self::setId(md5(self::getId()));
  367. // Force a regenerate after session is started
  368. self::$_regenerateIdState = -1;
  369. }
  370. if (self::$_sessionStarted && self::$_destroyed) {
  371. require_once 'Zend/Session/Exception.php';
  372. throw new Zend_Session_Exception('The session was explicitly destroyed during this request, attempting to re-start is not allowed.');
  373. }
  374. if (self::$_sessionStarted) {
  375. return; // already started
  376. }
  377. // make sure our default options (at the least) have been set
  378. if (!self::$_defaultOptionsSet) {
  379. self::setOptions(is_array($options) ? $options : array());
  380. }
  381. // In strict mode, do not allow auto-starting Zend_Session, such as via "new Zend_Session_Namespace()"
  382. if (self::$_strict && $options === true) {
  383. /** @see Zend_Session_Exception */
  384. require_once 'Zend/Session/Exception.php';
  385. throw new Zend_Session_Exception('You must explicitly start the session with Zend_Session::start() when session options are set to strict.');
  386. }
  387. $filename = $linenum = null;
  388. if (!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {
  389. /** @see Zend_Session_Exception */
  390. require_once 'Zend/Session/Exception.php';
  391. throw new Zend_Session_Exception("Session must be started before any output has been sent to the browser;"
  392. . " output started in {$filename}/{$linenum}");
  393. }
  394. // See http://www.php.net/manual/en/ref.session.php for explanation
  395. if (!self::$_unitTestEnabled && defined('SID')) {
  396. /** @see Zend_Session_Exception */
  397. require_once 'Zend/Session/Exception.php';
  398. throw new Zend_Session_Exception('session has already been started by session.auto-start or session_start()');
  399. }
  400. /**
  401. * Hack to throw exceptions on start instead of php errors
  402. * @see http://framework.zend.com/issues/browse/ZF-1325
  403. */
  404. $errorLevel = (is_int(self::$_throwStartupExceptions)) ? self::$_throwStartupExceptions : E_ALL;
  405. /** @see Zend_Session_Exception */
  406. if (!self::$_unitTestEnabled) {
  407. if (self::$_throwStartupExceptions) {
  408. require_once 'Zend/Session/Exception.php';
  409. set_error_handler(array('Zend_Session_Exception', 'handleSessionStartError'), $errorLevel);
  410. }
  411. $startedCleanly = session_start();
  412. if (self::$_throwStartupExceptions) {
  413. restore_error_handler();
  414. }
  415. if (!$startedCleanly || Zend_Session_Exception::$sessionStartError != null) {
  416. if (self::$_throwStartupExceptions) {
  417. set_error_handler(array('Zend_Session_Exception', 'handleSilentWriteClose'), $errorLevel);
  418. }
  419. session_write_close();
  420. if (self::$_throwStartupExceptions) {
  421. restore_error_handler();
  422. throw new Zend_Session_Exception(__CLASS__ . '::' . __FUNCTION__ . '() - ' . Zend_Session_Exception::$sessionStartError);
  423. }
  424. }
  425. }
  426. parent::$_readable = true;
  427. parent::$_writable = true;
  428. self::$_sessionStarted = true;
  429. if (self::$_regenerateIdState === -1) {
  430. self::regenerateId();
  431. }
  432. // run validators if they exist
  433. if (isset($_SESSION['__ZF']['VALID'])) {
  434. self::_processValidators();
  435. }
  436. self::_processStartupMetadataGlobal();
  437. }
  438. /**
  439. * Perform a hash-bits check on the session ID
  440. *
  441. * @param string $id Session ID
  442. * @return bool
  443. */
  444. protected static function _checkId($id)
  445. {
  446. $saveHandler = ini_get('session.save_handler');
  447. if ($saveHandler == 'cluster') { // Zend Server SC, validate only after last dash
  448. $dashPos = strrpos($id, '-');
  449. if ($dashPos) {
  450. $id = substr($id, $dashPos + 1);
  451. }
  452. }
  453. $hashBitsPerChar = ini_get('session.hash_bits_per_character');
  454. if (!$hashBitsPerChar) {
  455. $hashBitsPerChar = 5; // the default value
  456. }
  457. switch($hashBitsPerChar) {
  458. case 4: $pattern = '^[0-9a-f]*$'; break;
  459. case 5: $pattern = '^[0-9a-v]*$'; break;
  460. case 6: $pattern = '^[0-9a-zA-Z-,]*$'; break;
  461. }
  462. return preg_match('#'.$pattern.'#', $id);
  463. }
  464. /**
  465. * _processGlobalMetadata() - this method initizes the sessions GLOBAL
  466. * metadata, mostly global data expiration calculations.
  467. *
  468. * @return void
  469. */
  470. private static function _processStartupMetadataGlobal()
  471. {
  472. // process global metadata
  473. if (isset($_SESSION['__ZF'])) {
  474. // expire globally expired values
  475. foreach ($_SESSION['__ZF'] as $namespace => $namespace_metadata) {
  476. // Expire Namespace by Time (ENT)
  477. if (isset($namespace_metadata['ENT']) && ($namespace_metadata['ENT'] > 0) && (time() > $namespace_metadata['ENT']) ) {
  478. unset($_SESSION[$namespace]);
  479. unset($_SESSION['__ZF'][$namespace]);
  480. }
  481. // Expire Namespace by Global Hop (ENGH) if it wasnt expired above
  482. if (isset($_SESSION['__ZF'][$namespace]) && isset($namespace_metadata['ENGH']) && $namespace_metadata['ENGH'] >= 1) {
  483. $_SESSION['__ZF'][$namespace]['ENGH']--;
  484. if ($_SESSION['__ZF'][$namespace]['ENGH'] === 0) {
  485. if (isset($_SESSION[$namespace])) {
  486. parent::$_expiringData[$namespace] = $_SESSION[$namespace];
  487. unset($_SESSION[$namespace]);
  488. }
  489. unset($_SESSION['__ZF'][$namespace]);
  490. }
  491. }
  492. // Expire Namespace Variables by Time (ENVT)
  493. if (isset($namespace_metadata['ENVT'])) {
  494. foreach ($namespace_metadata['ENVT'] as $variable => $time) {
  495. if (time() > $time) {
  496. unset($_SESSION[$namespace][$variable]);
  497. unset($_SESSION['__ZF'][$namespace]['ENVT'][$variable]);
  498. }
  499. }
  500. if (empty($_SESSION['__ZF'][$namespace]['ENVT'])) {
  501. unset($_SESSION['__ZF'][$namespace]['ENVT']);
  502. }
  503. }
  504. // Expire Namespace Variables by Global Hop (ENVGH)
  505. if (isset($namespace_metadata['ENVGH'])) {
  506. foreach ($namespace_metadata['ENVGH'] as $variable => $hops) {
  507. $_SESSION['__ZF'][$namespace]['ENVGH'][$variable]--;
  508. if ($_SESSION['__ZF'][$namespace]['ENVGH'][$variable] === 0) {
  509. if (isset($_SESSION[$namespace][$variable])) {
  510. parent::$_expiringData[$namespace][$variable] = $_SESSION[$namespace][$variable];
  511. unset($_SESSION[$namespace][$variable]);
  512. }
  513. unset($_SESSION['__ZF'][$namespace]['ENVGH'][$variable]);
  514. }
  515. }
  516. if (empty($_SESSION['__ZF'][$namespace]['ENVGH'])) {
  517. unset($_SESSION['__ZF'][$namespace]['ENVGH']);
  518. }
  519. }
  520. if (isset($namespace) && empty($_SESSION['__ZF'][$namespace])) {
  521. unset($_SESSION['__ZF'][$namespace]);
  522. }
  523. }
  524. }
  525. if (isset($_SESSION['__ZF']) && empty($_SESSION['__ZF'])) {
  526. unset($_SESSION['__ZF']);
  527. }
  528. }
  529. /**
  530. * isStarted() - convenience method to determine if the session is already started.
  531. *
  532. * @return bool
  533. */
  534. public static function isStarted()
  535. {
  536. return self::$_sessionStarted;
  537. }
  538. /**
  539. * isRegenerated() - convenience method to determine if session_regenerate_id()
  540. * has been called during this request by Zend_Session.
  541. *
  542. * @return bool
  543. */
  544. public static function isRegenerated()
  545. {
  546. return ( (self::$_regenerateIdState > 0) ? true : false );
  547. }
  548. /**
  549. * getId() - get the current session id
  550. *
  551. * @return string
  552. */
  553. public static function getId()
  554. {
  555. return session_id();
  556. }
  557. /**
  558. * setId() - set an id to a user specified id
  559. *
  560. * @throws Zend_Session_Exception
  561. * @param string $id
  562. * @return void
  563. */
  564. public static function setId($id)
  565. {
  566. if (!self::$_unitTestEnabled && defined('SID')) {
  567. /** @see Zend_Session_Exception */
  568. require_once 'Zend/Session/Exception.php';
  569. throw new Zend_Session_Exception('The session has already been started. The session id must be set first.');
  570. }
  571. if (!self::$_unitTestEnabled && headers_sent($filename, $linenum)) {
  572. /** @see Zend_Session_Exception */
  573. require_once 'Zend/Session/Exception.php';
  574. throw new Zend_Session_Exception("You must call ".__CLASS__.'::'.__FUNCTION__.
  575. "() before any output has been sent to the browser; output started in {$filename}/{$linenum}");
  576. }
  577. if (!is_string($id) || $id === '') {
  578. /** @see Zend_Session_Exception */
  579. require_once 'Zend/Session/Exception.php';
  580. throw new Zend_Session_Exception('You must provide a non-empty string as a session identifier.');
  581. }
  582. session_id($id);
  583. }
  584. /**
  585. * registerValidator() - register a validator that will attempt to validate this session for
  586. * every future request
  587. *
  588. * @param Zend_Session_Validator_Interface $validator
  589. * @return void
  590. */
  591. public static function registerValidator(Zend_Session_Validator_Interface $validator)
  592. {
  593. $validator->setup();
  594. }
  595. /**
  596. * stop() - Disable write access. Optionally disable read (not implemented).
  597. *
  598. * @return void
  599. */
  600. public static function stop()
  601. {
  602. parent::$_writable = false;
  603. }
  604. /**
  605. * writeClose() - Shutdown the sesssion, close writing and detach $_SESSION from the back-end storage mechanism.
  606. * This will complete the internal data transformation on this request.
  607. *
  608. * @param bool $readonly - OPTIONAL remove write access (i.e. throw error if Zend_Session's attempt writes)
  609. * @return void
  610. */
  611. public static function writeClose($readonly = true)
  612. {
  613. if (self::$_unitTestEnabled) {
  614. return;
  615. }
  616. if (self::$_writeClosed) {
  617. return;
  618. }
  619. if ($readonly) {
  620. parent::$_writable = false;
  621. }
  622. session_write_close();
  623. self::$_writeClosed = true;
  624. }
  625. /**
  626. * destroy() - This is used to destroy session data, and optionally, the session cookie itself
  627. *
  628. * @param bool $remove_cookie - OPTIONAL remove session id cookie, defaults to true (remove cookie)
  629. * @param bool $readonly - OPTIONAL remove write access (i.e. throw error if Zend_Session's attempt writes)
  630. * @return void
  631. */
  632. public static function destroy($remove_cookie = true, $readonly = true)
  633. {
  634. if (self::$_unitTestEnabled) {
  635. return;
  636. }
  637. if (self::$_destroyed) {
  638. return;
  639. }
  640. if ($readonly) {
  641. parent::$_writable = false;
  642. }
  643. session_destroy();
  644. self::$_destroyed = true;
  645. if ($remove_cookie) {
  646. self::expireSessionCookie();
  647. }
  648. }
  649. /**
  650. * expireSessionCookie() - Sends an expired session id cookie, causing the client to delete the session cookie
  651. *
  652. * @return void
  653. */
  654. public static function expireSessionCookie()
  655. {
  656. if (self::$_unitTestEnabled) {
  657. return;
  658. }
  659. if (self::$_sessionCookieDeleted) {
  660. return;
  661. }
  662. self::$_sessionCookieDeleted = true;
  663. if (isset($_COOKIE[session_name()])) {
  664. $cookie_params = session_get_cookie_params();
  665. setcookie(
  666. session_name(),
  667. false,
  668. 315554400, // strtotime('1980-01-01'),
  669. $cookie_params['path'],
  670. $cookie_params['domain'],
  671. $cookie_params['secure']
  672. );
  673. }
  674. }
  675. /**
  676. * _processValidator() - internal function that is called in the existence of VALID metadata
  677. *
  678. * @throws Zend_Session_Exception
  679. * @return void
  680. */
  681. private static function _processValidators()
  682. {
  683. foreach ($_SESSION['__ZF']['VALID'] as $validator_name => $valid_data) {
  684. if (!class_exists($validator_name)) {
  685. require_once 'Zend/Loader.php';
  686. Zend_Loader::loadClass($validator_name);
  687. }
  688. $validator = new $validator_name;
  689. if ($validator->validate() === false) {
  690. /** @see Zend_Session_Exception */
  691. require_once 'Zend/Session/Exception.php';
  692. throw new Zend_Session_Exception("This session is not valid according to {$validator_name}.");
  693. }
  694. }
  695. }
  696. /**
  697. * namespaceIsset() - check to see if a namespace is set
  698. *
  699. * @param string $namespace
  700. * @return bool
  701. */
  702. public static function namespaceIsset($namespace)
  703. {
  704. return parent::_namespaceIsset($namespace);
  705. }
  706. /**
  707. * namespaceUnset() - unset a namespace or a variable within a namespace
  708. *
  709. * @param string $namespace
  710. * @throws Zend_Session_Exception
  711. * @return void
  712. */
  713. public static function namespaceUnset($namespace)
  714. {
  715. parent::_namespaceUnset($namespace);
  716. Zend_Session_Namespace::resetSingleInstance($namespace);
  717. }
  718. /**
  719. * namespaceGet() - get all variables in a namespace
  720. * Deprecated: Use getIterator() in Zend_Session_Namespace.
  721. *
  722. * @param string $namespace
  723. * @return array
  724. */
  725. public static function namespaceGet($namespace)
  726. {
  727. return parent::_namespaceGetAll($namespace);
  728. }
  729. /**
  730. * getIterator() - return an iteratable object for use in foreach and the like,
  731. * this completes the IteratorAggregate interface
  732. *
  733. * @throws Zend_Session_Exception
  734. * @return ArrayObject
  735. */
  736. public static function getIterator()
  737. {
  738. if (parent::$_readable === false) {
  739. /** @see Zend_Session_Exception */
  740. require_once 'Zend/Session/Exception.php';
  741. throw new Zend_Session_Exception(parent::_THROW_NOT_READABLE_MSG);
  742. }
  743. $spaces = array();
  744. if (isset($_SESSION)) {
  745. $spaces = array_keys($_SESSION);
  746. foreach($spaces as $key => $space) {
  747. if (!strncmp($space, '__', 2) || !is_array($_SESSION[$space])) {
  748. unset($spaces[$key]);
  749. }
  750. }
  751. }
  752. return new ArrayObject(array_merge($spaces, array_keys(parent::$_expiringData)));
  753. }
  754. /**
  755. * isWritable() - returns a boolean indicating if namespaces can write (use setters)
  756. *
  757. * @return bool
  758. */
  759. public static function isWritable()
  760. {
  761. return parent::$_writable;
  762. }
  763. /**
  764. * isReadable() - returns a boolean indicating if namespaces can write (use setters)
  765. *
  766. * @return bool
  767. */
  768. public static function isReadable()
  769. {
  770. return parent::$_readable;
  771. }
  772. }