PageRenderTime 47ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/Zend/Session/SessionManagerTest.php

http://github.com/zendframework/zf2
PHP | 637 lines | 450 code | 61 blank | 126 comment | 33 complexity | ba32701791e249778d1b695ab7c65961 MD5 | raw file
Possible License(s): BSD-3-Clause
  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. * @subpackage UnitTests
  18. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id:$
  21. */
  22. namespace ZendTest\Session;
  23. use Zend\Session\SessionManager,
  24. Zend\Session,
  25. Zend\Registry;
  26. /**
  27. * @category Zend
  28. * @package Zend_Session
  29. * @subpackage UnitTests
  30. * @group Zend_Session
  31. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  32. * @license http://framework.zend.com/license/new-bsd New BSD License
  33. */
  34. class SessionManagerTest extends \PHPUnit_Framework_TestCase
  35. {
  36. public $error;
  37. public $cookieDateFormat = 'D, d-M-y H:i:s e';
  38. public function setUp()
  39. {
  40. $this->forceAutoloader();
  41. $this->error = false;
  42. $this->manager = new SessionManager();
  43. Registry::_unsetInstance();
  44. }
  45. protected function forceAutoloader()
  46. {
  47. $splAutoloadFunctions = spl_autoload_functions();
  48. if (!$splAutoloadFunctions || !in_array('ZendTest_Autoloader', $splAutoloadFunctions)) {
  49. include __DIR__ . '/../../_autoload.php';
  50. }
  51. }
  52. /**
  53. * Hack to allow running tests in separate processes
  54. *
  55. * @see http://matthewturland.com/2010/08/19/process-isolation-in-phpunit/
  56. * @param PHPUnit_Framework_TestResult $result
  57. * @return void
  58. */
  59. public function run(\PHPUnit_Framework_TestResult $result = NULL)
  60. {
  61. $this->setPreserveGlobalState(false);
  62. return parent::run($result);
  63. }
  64. public function handleErrors($errno, $errstr)
  65. {
  66. $this->error = $errstr;
  67. }
  68. public function getTimestampFromCookie($cookie)
  69. {
  70. if (preg_match('/expires=([^;]+)/', $cookie, $matches)) {
  71. $ts = new \DateTime($matches[1]);
  72. return $ts;
  73. }
  74. return false;
  75. }
  76. public function testManagerUsesSessionConfigurationByDefault()
  77. {
  78. $config = $this->manager->getConfig();
  79. $this->assertTrue($config instanceof Session\Configuration\SessionConfiguration);
  80. }
  81. public function testCanPassConfigurationToConstructor()
  82. {
  83. $config = new Session\Configuration\StandardConfiguration();
  84. $manager = new SessionManager($config);
  85. $this->assertSame($config, $manager->getConfig());
  86. }
  87. public function testPassingUnknownStringClassForConfigurationRaisesException()
  88. {
  89. $this->setExpectedException('Zend\Session\Exception\InvalidArgumentException', 'Configuration class provided is invalid; not found');
  90. $manager = new SessionManager('foobarbazbat');
  91. }
  92. public function testPassingInvalidStringClassForConfigurationRaisesException()
  93. {
  94. $this->setExpectedException('Zend\Session\Exception\InvalidArgumentException', 'Configuration type provided is invalid; must implement Zend\Session\Configuration');
  95. $manager = new SessionManager('Zend\Session\Storage\ArrayStorage');
  96. }
  97. public function testPassingValidStringClassForConfigurationInstantiatesThatConfiguration()
  98. {
  99. $manager = new SessionManager('Zend\\Session\\Configuration\\StandardConfiguration');
  100. $config = $manager->getConfig();
  101. $this->assertTrue($config instanceof Session\Configuration\StandardConfiguration);
  102. }
  103. public function testPassingValidStringClassInClassKeyOfArrayConfigurationInstantiatesThatConfiguration()
  104. {
  105. $manager = new SessionManager(array('class' => 'Zend\\Session\\Configuration\\StandardConfiguration'));
  106. $config = $manager->getConfig();
  107. $this->assertTrue($config instanceof Session\Configuration\StandardConfiguration);
  108. }
  109. public function testPassingInvalidStringClassInClassKeyOfArrayConfigurationRaisesException()
  110. {
  111. $this->setExpectedException('Zend\Session\Exception\InvalidArgumentException', 'Class provided for configuration is invalid; not found');
  112. $manager = new SessionManager(array('class' => 'foobarbaz'));
  113. }
  114. public function testPassingValidStringClassInClassKeyOfArrayConfigurationInstantiatesThatConfigurationWithOptionsProvided()
  115. {
  116. $manager = new SessionManager(array(
  117. 'class' => 'Zend\\Session\\Configuration\\StandardConfiguration',
  118. 'save_path' => __DIR__,
  119. ));
  120. $config = $manager->getConfig();
  121. $this->assertTrue($config instanceof Session\Configuration\StandardConfiguration);
  122. $this->assertEquals(__DIR__, $config->getSavePath());
  123. }
  124. public function testPassingZendConfigObjectForConfigurationInstantiatesThatConfiguration()
  125. {
  126. $config = new \Zend\Config\Config(array(
  127. 'class' => 'Zend\\Session\\Configuration\\StandardConfiguration',
  128. 'save_path' => __DIR__,
  129. ));
  130. $manager = new SessionManager($config);
  131. $config = $manager->getConfig();
  132. $this->assertTrue($config instanceof Session\Configuration\StandardConfiguration);
  133. $this->assertEquals(__DIR__, $config->getSavePath());
  134. }
  135. public function testManagerUsesSessionStorageByDefault()
  136. {
  137. $storage = $this->manager->getStorage();
  138. $this->assertTrue($storage instanceof Session\Storage\SessionStorage);
  139. }
  140. public function testCanPassStorageToConstructor()
  141. {
  142. $storage = new Session\Storage\ArrayStorage();
  143. $manager = new SessionManager(null, $storage);
  144. $this->assertSame($storage, $manager->getStorage());
  145. }
  146. public function testCanPassStringStorageNameToConstructor()
  147. {
  148. $manager = new SessionManager(null, 'Zend\\Session\\Storage\\ArrayStorage');
  149. $storage = $manager->getStorage();
  150. $this->assertTrue($storage instanceof Session\Storage\ArrayStorage);
  151. }
  152. public function testCanPassStorageClassToConfigurationOptions()
  153. {
  154. $manager = new SessionManager(array('storage' => 'Zend\\Session\\Storage\\ArrayStorage'));
  155. $storage = $manager->getStorage();
  156. $this->assertTrue($storage instanceof Session\Storage\ArrayStorage);
  157. }
  158. public function testPassingStorageViaParamOverridesStorageInConfig()
  159. {
  160. $storage = new Session\Storage\ArrayStorage();
  161. $manager = new TestAsset\TestManager(array(
  162. 'class' => 'Zend\\Session\\Configuration\\StandardConfiguration',
  163. 'storage' => 'Zend\\Session\\Storage\\SessionStorage',
  164. ), $storage);
  165. $this->assertSame($storage, $manager->getStorage());
  166. }
  167. // Session-related functionality
  168. /**
  169. * @runInSeparateProcess
  170. */
  171. public function testSessionExistsReturnsFalseWhenNoSessionStarted()
  172. {
  173. $this->assertFalse($this->manager->sessionExists());
  174. }
  175. /**
  176. * @runInSeparateProcess
  177. */
  178. public function testSessionExistsReturnsTrueWhenSessionStarted()
  179. {
  180. session_start();
  181. $this->assertTrue($this->manager->sessionExists());
  182. }
  183. /**
  184. * @runInSeparateProcess
  185. */
  186. public function testSessionExistsReturnsTrueWhenSessionStartedThenWritten()
  187. {
  188. session_start();
  189. session_write_close();
  190. $this->assertTrue($this->manager->sessionExists());
  191. }
  192. /**
  193. * @runInSeparateProcess
  194. */
  195. public function testSessionExistsReturnsFalseWhenSessionStartedThenDestroyed()
  196. {
  197. session_start();
  198. session_destroy();
  199. $this->assertFalse($this->manager->sessionExists());
  200. }
  201. /**
  202. * @runInSeparateProcess
  203. */
  204. public function testSessionIsStartedAfterCallingStart()
  205. {
  206. $this->assertFalse($this->manager->sessionExists());
  207. $this->manager->start();
  208. $this->assertTrue($this->manager->sessionExists());
  209. }
  210. /**
  211. * @runInSeparateProcess
  212. */
  213. public function testStartDoesNothingWhenCalledAfterWriteCloseOperation()
  214. {
  215. $this->manager->start();
  216. $id1 = session_id();
  217. session_write_close();
  218. $this->manager->start();
  219. $id2 = session_id();
  220. $this->assertTrue($this->manager->sessionExists());
  221. $this->assertEquals($id1, $id2);
  222. }
  223. /**
  224. * @runInSeparateProcess
  225. */
  226. public function testStartCreatesNewSessionIfPreviousSessionHasBeenDestroyed()
  227. {
  228. $this->manager->start();
  229. $id1 = session_id();
  230. session_destroy();
  231. $this->manager->start();
  232. $id2 = session_id();
  233. $this->assertTrue($this->manager->sessionExists());
  234. $this->assertNotEquals($id1, $id2);
  235. }
  236. /**
  237. * @outputBuffering disabled
  238. */
  239. public function testStartWillNotBlockHeaderSentNotices()
  240. {
  241. if ('cli' == PHP_SAPI) {
  242. $this->markTestSkipped('session_start() will not raise headers_sent warnings in CLI');
  243. }
  244. set_error_handler(array($this, 'handleErrors'), E_WARNING);
  245. echo ' ';
  246. $this->assertTrue(headers_sent());
  247. $this->manager->start();
  248. restore_error_handler();
  249. $this->assertTrue(is_string($this->error));
  250. $this->assertContains('already sent', $this->error);
  251. }
  252. /**
  253. * @runInSeparateProcess
  254. */
  255. public function testGetNameReturnsSessionName()
  256. {
  257. $ini = ini_get('session.name');
  258. $this->assertEquals($ini, $this->manager->getName());
  259. }
  260. /**
  261. * @runInSeparateProcess
  262. */
  263. public function testSetNameRaisesExceptionOnInvalidName()
  264. {
  265. $this->setExpectedException('Zend\Session\Exception\InvalidArgumentException', 'Name provided contains invalid characters; must be alphanumeric only');
  266. $this->manager->setName('foo bar!');
  267. }
  268. /**
  269. * @runInSeparateProcess
  270. */
  271. public function testSetNameSetsSessionNameOnSuccess()
  272. {
  273. $this->manager->setName('foobar');
  274. $this->assertEquals('foobar', $this->manager->getName());
  275. $this->assertEquals('foobar', session_name());
  276. }
  277. /**
  278. * @runInSeparateProcess
  279. */
  280. public function testCanSetNewSessionNameAfterSessionDestroyed()
  281. {
  282. $this->manager->start();
  283. session_destroy();
  284. $this->manager->setName('foobar');
  285. $this->assertEquals('foobar', $this->manager->getName());
  286. $this->assertEquals('foobar', session_name());
  287. }
  288. /**
  289. * @runInSeparateProcess
  290. */
  291. public function testSettingNameWhenAnActiveSessionExistsRaisesException()
  292. {
  293. $this->setExpectedException('Zend\Session\Exception\InvalidArgumentException', 'Cannot set session name after a session has already started');
  294. $this->manager->start();
  295. $this->manager->setName('foobar');
  296. }
  297. /**
  298. * @runInSeparateProcess
  299. */
  300. public function testDestroyByDefaultSendsAnExpireCookie()
  301. {
  302. if (!extension_loaded('xdebug')) {
  303. $this->markTestSkipped('Xdebug required for this test');
  304. }
  305. $config = $this->manager->getConfig();
  306. $config->setUseCookies(true);
  307. $this->manager->start();
  308. $this->manager->destroy();
  309. echo '';
  310. $headers = xdebug_get_headers();
  311. $found = false;
  312. $sName = $this->manager->getName();
  313. foreach ($headers as $header) {
  314. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName)) {
  315. $found = true;
  316. }
  317. }
  318. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  319. }
  320. /**
  321. * @runInSeparateProcess
  322. */
  323. public function testSendingFalseToSendExpireCookieWhenCallingDestroyShouldNotSendCookie()
  324. {
  325. if (!extension_loaded('xdebug')) {
  326. $this->markTestSkipped('Xdebug required for this test');
  327. }
  328. $config = $this->manager->getConfig();
  329. $config->setUseCookies(true);
  330. $this->manager->start();
  331. $this->manager->destroy(array('send_expire_cookie' => false));
  332. echo '';
  333. $headers = xdebug_get_headers();
  334. $found = false;
  335. $sName = $this->manager->getName();
  336. foreach ($headers as $header) {
  337. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName)) {
  338. $found = true;
  339. }
  340. }
  341. if ($found) {
  342. $this->assertNotContains('expires=', $header);
  343. } else {
  344. $this->assertFalse($found, 'Unexpected session cookie found: ' . var_export($headers, true));
  345. }
  346. }
  347. /**
  348. * @runInSeparateProcess
  349. */
  350. public function testDestroyDoesNotClearSessionStorageByDefault()
  351. {
  352. $this->manager->start();
  353. $storage = $this->manager->getStorage();
  354. $storage['foo'] = 'bar';
  355. $this->manager->destroy();
  356. $this->assertTrue(isset($storage['foo']));
  357. $this->assertEquals('bar', $storage['foo']);
  358. }
  359. /**
  360. * @runInSeparateProcess
  361. */
  362. public function testPassingClearStorageOptionWhenCallingDestroyClearsStorage()
  363. {
  364. $this->manager->start();
  365. $storage = $this->manager->getStorage();
  366. $storage['foo'] = 'bar';
  367. $this->manager->destroy(array('clear_storage' => true));
  368. $this->assertSame(array(), (array) $storage);
  369. }
  370. /**
  371. * @runInSeparateProcess
  372. */
  373. public function testCallingWriteCloseMarksStorageAsImmutable()
  374. {
  375. $this->manager->start();
  376. $storage = $this->manager->getStorage();
  377. $storage['foo'] = 'bar';
  378. $this->manager->writeClose();
  379. $this->assertTrue($storage->isImmutable());
  380. }
  381. /**
  382. * @runInSeparateProcess
  383. */
  384. public function testCallingWriteCloseShouldNotAlterSessionExistsStatus()
  385. {
  386. $this->manager->start();
  387. $this->manager->writeClose();
  388. $this->assertTrue($this->manager->sessionExists());
  389. }
  390. /**
  391. * @runInSeparateProcess
  392. */
  393. public function testIdShouldBeEmptyPriorToCallingStart()
  394. {
  395. $this->assertSame('', $this->manager->getId());
  396. }
  397. /**
  398. * @runInSeparateProcess
  399. */
  400. public function testIdShouldBeMutablePriorToCallingStart()
  401. {
  402. $this->manager->setId(__CLASS__);
  403. $this->assertSame(__CLASS__, $this->manager->getId());
  404. $this->assertSame(__CLASS__, session_id());
  405. }
  406. /**
  407. * @runInSeparateProcess
  408. */
  409. public function testIdShouldBeMutablePriorAfterSessionStarted()
  410. {
  411. $this->manager->start();
  412. $origId = $this->manager->getId();
  413. $this->manager->setId(__METHOD__);
  414. $this->assertNotSame($origId, $this->manager->getId());
  415. $this->assertSame(__METHOD__, $this->manager->getId());
  416. $this->assertSame(__METHOD__, session_id());
  417. }
  418. /**
  419. * @runInSeparateProcess
  420. */
  421. public function testSettingIdAfterSessionStartedShouldSendExpireCookie()
  422. {
  423. if (!extension_loaded('xdebug')) {
  424. $this->markTestSkipped('Xdebug required for this test');
  425. }
  426. $config = $this->manager->getConfig();
  427. $config->setUseCookies(true);
  428. $this->manager->start();
  429. $origId = $this->manager->getId();
  430. $this->manager->setId(__METHOD__);
  431. $headers = xdebug_get_headers();
  432. $found = false;
  433. $sName = $this->manager->getName();
  434. foreach ($headers as $header) {
  435. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName)) {
  436. $found = true;
  437. }
  438. }
  439. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  440. }
  441. /**
  442. * @runInSeparateProcess
  443. */
  444. public function testRegenerateIdShouldWorkAfterSessionStarted()
  445. {
  446. $this->manager->start();
  447. $origId = $this->manager->getId();
  448. $this->manager->regenerateId();
  449. $this->assertNotSame($origId, $this->manager->getId());
  450. }
  451. /**
  452. * @runInSeparateProcess
  453. */
  454. public function testRegeneratingIdAfterSessionStartedShouldSendExpireCookie()
  455. {
  456. if (!extension_loaded('xdebug')) {
  457. $this->markTestSkipped('Xdebug required for this test');
  458. }
  459. $config = $this->manager->getConfig();
  460. $config->setUseCookies(true);
  461. $this->manager->start();
  462. $origId = $this->manager->getId();
  463. $this->manager->regenerateId();
  464. $headers = xdebug_get_headers();
  465. $found = false;
  466. $sName = $this->manager->getName();
  467. foreach ($headers as $header) {
  468. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName)) {
  469. $found = true;
  470. }
  471. }
  472. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  473. }
  474. /**
  475. * @runInSeparateProcess
  476. */
  477. public function testRememberMeShouldSendNewSessionCookieWithUpdatedTimestamp()
  478. {
  479. if (!extension_loaded('xdebug')) {
  480. $this->markTestSkipped('Xdebug required for this test');
  481. }
  482. $config = $this->manager->getConfig();
  483. $config->setUseCookies(true);
  484. $this->manager->start();
  485. $this->manager->rememberMe(18600);
  486. $headers = xdebug_get_headers();
  487. $found = false;
  488. $sName = $this->manager->getName();
  489. $cookie = false;
  490. foreach ($headers as $header) {
  491. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName) && !stristr($header, '=deleted')) {
  492. $found = true;
  493. $cookie = $header;
  494. }
  495. }
  496. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  497. $ts = $this->getTimestampFromCookie($cookie);
  498. if (!$ts) {
  499. $this->fail('Cookie did not contain expiry? ' . var_export($headers, true));
  500. }
  501. $this->assertGreaterThan($_SERVER['REQUEST_TIME'], $ts->getTimestamp(), 'Session cookie: ' . var_export($headers, 1));
  502. }
  503. /**
  504. * @runInSeparateProcess
  505. */
  506. public function testRememberMeShouldSetTimestampBasedOnConfigurationByDefault()
  507. {
  508. if (!extension_loaded('xdebug')) {
  509. $this->markTestSkipped('Xdebug required for this test');
  510. }
  511. $config = $this->manager->getConfig();
  512. $config->setUseCookies(true);
  513. $config->setRememberMeSeconds(3600);
  514. $ttl = $config->getRememberMeSeconds();
  515. $this->manager->start();
  516. $this->manager->rememberMe();
  517. $headers = xdebug_get_headers();
  518. $found = false;
  519. $sName = $this->manager->getName();
  520. $cookie = false;
  521. foreach ($headers as $header) {
  522. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName) && !stristr($header, '=deleted')) {
  523. $found = true;
  524. $cookie = $header;
  525. }
  526. }
  527. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  528. $ts = $this->getTimestampFromCookie($cookie);
  529. if (!$ts) {
  530. $this->fail('Cookie did not contain expiry? ' . var_export($headers, true));
  531. }
  532. $compare = $_SERVER['REQUEST_TIME'] + $ttl;
  533. $cookieTs = $ts->getTimestamp();
  534. $this->assertTrue(in_array($cookieTs, range($compare, $compare + 10)), 'Session cookie: ' . var_export($headers, 1));
  535. }
  536. /**
  537. * @runInSeparateProcess
  538. */
  539. public function testForgetMeShouldSendCookieWithZeroTimestamp()
  540. {
  541. if (!extension_loaded('xdebug')) {
  542. $this->markTestSkipped('Xdebug required for this test');
  543. }
  544. $config = $this->manager->getConfig();
  545. $config->setUseCookies(true);
  546. $this->manager->start();
  547. $this->manager->forgetMe();
  548. $headers = xdebug_get_headers();
  549. $found = false;
  550. $sName = $this->manager->getName();
  551. foreach ($headers as $header) {
  552. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName) && !stristr($header, '=deleted')) {
  553. $found = true;
  554. }
  555. }
  556. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  557. $this->assertNotContains('expires=', $header);
  558. }
  559. /**
  560. * @runInSeparateProcess
  561. */
  562. public function testStartingSessionThatFailsAValidatorShouldRaiseException()
  563. {
  564. $chain = $this->manager->getValidatorChain();
  565. $chain->attach('session.validate', array($this, 'validateSession'));
  566. $this->setExpectedException('Zend\Session\Exception\RuntimeException', 'failed');
  567. $this->manager->start();
  568. }
  569. /**
  570. * @see testStartingSessionThatFailsAValidatorShouldRaiseException()
  571. */
  572. public static function validateSession()
  573. {
  574. return false;
  575. }
  576. }