PageRenderTime 48ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/ZendTest/Session/SessionManagerTest.php

https://github.com/eiwaen/zf2
PHP | 554 lines | 387 code | 56 blank | 111 comment | 32 complexity | c98e74b043c9758cb27a81985c3f0e47 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. */
  9. namespace ZendTest\Session;
  10. use Zend\Session\SessionManager;
  11. use Zend\Session;
  12. /**
  13. * @group Zend_Session
  14. * @preserveGlobalState disabled
  15. */
  16. class SessionManagerTest extends \PHPUnit_Framework_TestCase
  17. {
  18. public $error;
  19. public $cookieDateFormat = 'D, d-M-y H:i:s e';
  20. /**
  21. * @var SessionManager
  22. */
  23. protected $manager;
  24. public function setUp()
  25. {
  26. $this->forceAutoloader();
  27. $this->error = false;
  28. $this->manager = new SessionManager();
  29. }
  30. protected function forceAutoloader()
  31. {
  32. $splAutoloadFunctions = spl_autoload_functions();
  33. if (!$splAutoloadFunctions || !in_array('ZendTest_Autoloader', $splAutoloadFunctions)) {
  34. include __DIR__ . '/../../_autoload.php';
  35. }
  36. }
  37. public function handleErrors($errno, $errstr)
  38. {
  39. $this->error = $errstr;
  40. }
  41. public function getTimestampFromCookie($cookie)
  42. {
  43. if (preg_match('/expires=([^;]+)/', $cookie, $matches)) {
  44. $ts = new \DateTime($matches[1]);
  45. return $ts;
  46. }
  47. return false;
  48. }
  49. public function testManagerUsesSessionConfigByDefault()
  50. {
  51. $config = $this->manager->getConfig();
  52. $this->assertTrue($config instanceof Session\Config\SessionConfig);
  53. }
  54. public function testCanPassConfigurationToConstructor()
  55. {
  56. $config = new Session\Config\StandardConfig();
  57. $manager = new SessionManager($config);
  58. $this->assertSame($config, $manager->getConfig());
  59. }
  60. public function testManagerUsesSessionStorageByDefault()
  61. {
  62. $storage = $this->manager->getStorage();
  63. $this->assertTrue($storage instanceof Session\Storage\SessionArrayStorage);
  64. }
  65. public function testCanPassStorageToConstructor()
  66. {
  67. $storage = new Session\Storage\ArrayStorage();
  68. $manager = new SessionManager(null, $storage);
  69. $this->assertSame($storage, $manager->getStorage());
  70. }
  71. public function testCanPassSaveHandlerToConstructor()
  72. {
  73. $saveHandler = new TestAsset\TestSaveHandler();
  74. $manager = new SessionManager(null, null, $saveHandler);
  75. $this->assertSame($saveHandler, $manager->getSaveHandler());
  76. }
  77. // Session-related functionality
  78. /**
  79. * @runInSeparateProcess
  80. */
  81. public function testSessionExistsReturnsFalseWhenNoSessionStarted()
  82. {
  83. $this->assertFalse($this->manager->sessionExists());
  84. }
  85. /**
  86. * @runInSeparateProcess
  87. */
  88. public function testSessionExistsReturnsTrueWhenSessionStarted()
  89. {
  90. session_start();
  91. $this->assertTrue($this->manager->sessionExists());
  92. }
  93. /**
  94. * @runInSeparateProcess
  95. */
  96. public function testSessionExistsReturnsTrueWhenSessionStartedThenWritten()
  97. {
  98. session_start();
  99. session_write_close();
  100. $this->assertTrue($this->manager->sessionExists());
  101. }
  102. /**
  103. * @runInSeparateProcess
  104. */
  105. public function testSessionExistsReturnsFalseWhenSessionStartedThenDestroyed()
  106. {
  107. session_start();
  108. session_destroy();
  109. $this->assertFalse($this->manager->sessionExists());
  110. }
  111. /**
  112. * @runInSeparateProcess
  113. */
  114. public function testSessionIsStartedAfterCallingStart()
  115. {
  116. $this->assertFalse($this->manager->sessionExists());
  117. $this->manager->start();
  118. $this->assertTrue($this->manager->sessionExists());
  119. }
  120. /**
  121. * @runInSeparateProcess
  122. */
  123. public function testStartDoesNothingWhenCalledAfterWriteCloseOperation()
  124. {
  125. $this->manager->start();
  126. $id1 = session_id();
  127. session_write_close();
  128. $this->manager->start();
  129. $id2 = session_id();
  130. $this->assertTrue($this->manager->sessionExists());
  131. $this->assertEquals($id1, $id2);
  132. }
  133. /**
  134. * @runInSeparateProcess
  135. */
  136. public function testStorageContentIsPreservedByWriteCloseOperation()
  137. {
  138. $this->manager->start();
  139. $storage = $this->manager->getStorage();
  140. $storage['foo'] = 'bar';
  141. $this->manager->writeClose();
  142. $this->assertTrue(isset($storage['foo']) && $storage['foo'] == 'bar');
  143. }
  144. /**
  145. * @runInSeparateProcess
  146. */
  147. public function testStartCreatesNewSessionIfPreviousSessionHasBeenDestroyed()
  148. {
  149. $this->manager->start();
  150. $id1 = session_id();
  151. session_destroy();
  152. $this->manager->start();
  153. $id2 = session_id();
  154. $this->assertTrue($this->manager->sessionExists());
  155. $this->assertNotEquals($id1, $id2);
  156. }
  157. /**
  158. * @outputBuffering disabled
  159. */
  160. public function testStartWillNotBlockHeaderSentNotices()
  161. {
  162. if ('cli' == PHP_SAPI) {
  163. $this->markTestSkipped('session_start() will not raise headers_sent warnings in CLI');
  164. }
  165. set_error_handler(array($this, 'handleErrors'), E_WARNING);
  166. echo ' ';
  167. $this->assertTrue(headers_sent());
  168. $this->manager->start();
  169. restore_error_handler();
  170. $this->assertTrue(is_string($this->error));
  171. $this->assertContains('already sent', $this->error);
  172. }
  173. /**
  174. * @runInSeparateProcess
  175. */
  176. public function testGetNameReturnsSessionName()
  177. {
  178. $ini = ini_get('session.name');
  179. $this->assertEquals($ini, $this->manager->getName());
  180. }
  181. /**
  182. * @runInSeparateProcess
  183. */
  184. public function testSetNameRaisesExceptionOnInvalidName()
  185. {
  186. $this->setExpectedException('Zend\Session\Exception\InvalidArgumentException', 'Name provided contains invalid characters; must be alphanumeric only');
  187. $this->manager->setName('foo bar!');
  188. }
  189. /**
  190. * @runInSeparateProcess
  191. */
  192. public function testSetNameSetsSessionNameOnSuccess()
  193. {
  194. $this->manager->setName('foobar');
  195. $this->assertEquals('foobar', $this->manager->getName());
  196. $this->assertEquals('foobar', session_name());
  197. }
  198. /**
  199. * @runInSeparateProcess
  200. */
  201. public function testCanSetNewSessionNameAfterSessionDestroyed()
  202. {
  203. $this->manager->start();
  204. session_destroy();
  205. $this->manager->setName('foobar');
  206. $this->assertEquals('foobar', $this->manager->getName());
  207. $this->assertEquals('foobar', session_name());
  208. }
  209. /**
  210. * @runInSeparateProcess
  211. */
  212. public function testSettingNameWhenAnActiveSessionExistsRaisesException()
  213. {
  214. $this->setExpectedException('Zend\Session\Exception\InvalidArgumentException',
  215. 'Cannot set session name after a session has already started');
  216. $this->manager->start();
  217. $this->manager->setName('foobar');
  218. }
  219. /**
  220. * @runInSeparateProcess
  221. */
  222. public function testDestroyByDefaultSendsAnExpireCookie()
  223. {
  224. if (!extension_loaded('xdebug')) {
  225. $this->markTestSkipped('Xdebug required for this test');
  226. }
  227. $config = $this->manager->getConfig();
  228. $config->setUseCookies(true);
  229. $this->manager->start();
  230. $this->manager->destroy();
  231. echo '';
  232. $headers = xdebug_get_headers();
  233. $found = false;
  234. $sName = $this->manager->getName();
  235. foreach ($headers as $header) {
  236. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName)) {
  237. $found = true;
  238. }
  239. }
  240. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  241. }
  242. /**
  243. * @runInSeparateProcess
  244. */
  245. public function testSendingFalseToSendExpireCookieWhenCallingDestroyShouldNotSendCookie()
  246. {
  247. if (!extension_loaded('xdebug')) {
  248. $this->markTestSkipped('Xdebug required for this test');
  249. }
  250. $config = $this->manager->getConfig();
  251. $config->setUseCookies(true);
  252. $this->manager->start();
  253. $this->manager->destroy(array('send_expire_cookie' => false));
  254. echo '';
  255. $headers = xdebug_get_headers();
  256. $found = false;
  257. $sName = $this->manager->getName();
  258. foreach ($headers as $header) {
  259. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName)) {
  260. $found = true;
  261. }
  262. }
  263. if ($found) {
  264. $this->assertNotContains('expires=', $header);
  265. } else {
  266. $this->assertFalse($found, 'Unexpected session cookie found: ' . var_export($headers, true));
  267. }
  268. }
  269. /**
  270. * @runInSeparateProcess
  271. */
  272. public function testDestroyDoesNotClearSessionStorageByDefault()
  273. {
  274. $this->manager->start();
  275. $storage = $this->manager->getStorage();
  276. $storage['foo'] = 'bar';
  277. $this->manager->destroy();
  278. $this->assertTrue(isset($storage['foo']));
  279. $this->assertEquals('bar', $storage['foo']);
  280. }
  281. /**
  282. * @runInSeparateProcess
  283. */
  284. public function testPassingClearStorageOptionWhenCallingDestroyClearsStorage()
  285. {
  286. $this->manager->start();
  287. $storage = $this->manager->getStorage();
  288. $storage['foo'] = 'bar';
  289. $this->manager->destroy(array('clear_storage' => true));
  290. $this->assertFalse(isset($storage['foo']));
  291. }
  292. /**
  293. * @runInSeparateProcess
  294. */
  295. public function testCallingWriteCloseMarksStorageAsImmutable()
  296. {
  297. $this->manager->start();
  298. $storage = $this->manager->getStorage();
  299. $storage['foo'] = 'bar';
  300. $this->manager->writeClose();
  301. $this->assertTrue($storage->isImmutable());
  302. }
  303. /**
  304. * @runInSeparateProcess
  305. */
  306. public function testCallingWriteCloseShouldNotAlterSessionExistsStatus()
  307. {
  308. $this->manager->start();
  309. $this->manager->writeClose();
  310. $this->assertTrue($this->manager->sessionExists());
  311. }
  312. /**
  313. * @runInSeparateProcess
  314. */
  315. public function testIdShouldBeEmptyPriorToCallingStart()
  316. {
  317. $this->assertSame('', $this->manager->getId());
  318. }
  319. /**
  320. * @runInSeparateProcess
  321. */
  322. public function testIdShouldBeMutablePriorToCallingStart()
  323. {
  324. $this->manager->setId(__CLASS__);
  325. $this->assertSame(__CLASS__, $this->manager->getId());
  326. $this->assertSame(__CLASS__, session_id());
  327. }
  328. /**
  329. * @runInSeparateProcess
  330. */
  331. public function testIdShouldNotBeMutableAfterSessionStarted()
  332. {
  333. $this->setExpectedException('RuntimeException',
  334. 'Session has already been started, to change the session ID call regenerateId()');
  335. $this->manager->start();
  336. $origId = $this->manager->getId();
  337. $this->manager->setId(__METHOD__);
  338. }
  339. /**
  340. * @runInSeparateProcess
  341. */
  342. public function testRegenerateIdShouldWorkAfterSessionStarted()
  343. {
  344. $this->manager->start();
  345. $origId = $this->manager->getId();
  346. $this->manager->regenerateId();
  347. $this->assertNotSame($origId, $this->manager->getId());
  348. }
  349. /**
  350. * @runInSeparateProcess
  351. */
  352. public function testRegeneratingIdAfterSessionStartedShouldSendExpireCookie()
  353. {
  354. if (!extension_loaded('xdebug')) {
  355. $this->markTestSkipped('Xdebug required for this test');
  356. }
  357. $config = $this->manager->getConfig();
  358. $config->setUseCookies(true);
  359. $this->manager->start();
  360. $origId = $this->manager->getId();
  361. $this->manager->regenerateId();
  362. $headers = xdebug_get_headers();
  363. $found = false;
  364. $sName = $this->manager->getName();
  365. foreach ($headers as $header) {
  366. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName)) {
  367. $found = true;
  368. }
  369. }
  370. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  371. }
  372. /**
  373. * @runInSeparateProcess
  374. */
  375. public function testRememberMeShouldSendNewSessionCookieWithUpdatedTimestamp()
  376. {
  377. if (!extension_loaded('xdebug')) {
  378. $this->markTestSkipped('Xdebug required for this test');
  379. }
  380. $config = $this->manager->getConfig();
  381. $config->setUseCookies(true);
  382. $this->manager->start();
  383. $this->manager->rememberMe(18600);
  384. $headers = xdebug_get_headers();
  385. $found = false;
  386. $sName = $this->manager->getName();
  387. $cookie = false;
  388. foreach ($headers as $header) {
  389. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName) && !stristr($header, '=deleted')) {
  390. $found = true;
  391. $cookie = $header;
  392. }
  393. }
  394. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  395. $ts = $this->getTimestampFromCookie($cookie);
  396. if (!$ts) {
  397. $this->fail('Cookie did not contain expiry? ' . var_export($headers, true));
  398. }
  399. $this->assertGreaterThan($_SERVER['REQUEST_TIME'], $ts->getTimestamp(), 'Session cookie: ' . var_export($headers, 1));
  400. }
  401. /**
  402. * @runInSeparateProcess
  403. */
  404. public function testRememberMeShouldSetTimestampBasedOnConfigurationByDefault()
  405. {
  406. if (!extension_loaded('xdebug')) {
  407. $this->markTestSkipped('Xdebug required for this test');
  408. }
  409. $config = $this->manager->getConfig();
  410. $config->setUseCookies(true);
  411. $config->setRememberMeSeconds(3600);
  412. $ttl = $config->getRememberMeSeconds();
  413. $this->manager->start();
  414. $this->manager->rememberMe();
  415. $headers = xdebug_get_headers();
  416. $found = false;
  417. $sName = $this->manager->getName();
  418. $cookie = false;
  419. foreach ($headers as $header) {
  420. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName) && !stristr($header, '=deleted')) {
  421. $found = true;
  422. $cookie = $header;
  423. }
  424. }
  425. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  426. $ts = $this->getTimestampFromCookie($cookie);
  427. if (!$ts) {
  428. $this->fail('Cookie did not contain expiry? ' . var_export($headers, true));
  429. }
  430. $compare = $_SERVER['REQUEST_TIME'] + $ttl;
  431. $cookieTs = $ts->getTimestamp();
  432. $this->assertTrue(in_array($cookieTs, range($compare, $compare + 10)), 'Session cookie: ' . var_export($headers, 1));
  433. }
  434. /**
  435. * @runInSeparateProcess
  436. */
  437. public function testForgetMeShouldSendCookieWithZeroTimestamp()
  438. {
  439. if (!extension_loaded('xdebug')) {
  440. $this->markTestSkipped('Xdebug required for this test');
  441. }
  442. $config = $this->manager->getConfig();
  443. $config->setUseCookies(true);
  444. $this->manager->start();
  445. $this->manager->forgetMe();
  446. $headers = xdebug_get_headers();
  447. $found = false;
  448. $sName = $this->manager->getName();
  449. foreach ($headers as $header) {
  450. if (stristr($header, 'Set-Cookie:') && stristr($header, $sName) && !stristr($header, '=deleted')) {
  451. $found = true;
  452. }
  453. }
  454. $this->assertTrue($found, 'No session cookie found: ' . var_export($headers, true));
  455. $this->assertNotContains('expires=', $header);
  456. }
  457. /**
  458. * @runInSeparateProcess
  459. */
  460. public function testStartingSessionThatFailsAValidatorShouldRaiseException()
  461. {
  462. $chain = $this->manager->getValidatorChain();
  463. $chain->attach('session.validate', array(new TestAsset\TestFailingValidator(), 'isValid'));
  464. $this->setExpectedException('Zend\Session\Exception\RuntimeException', 'failed');
  465. $this->manager->start();
  466. }
  467. /**
  468. * @runInSeparateProcess
  469. */
  470. public function testResumeSessionThatFailsAValidatorShouldRaiseException()
  471. {
  472. $this->manager->setSaveHandler(new TestAsset\TestSaveHandlerWithValidator);
  473. $this->setExpectedException('Zend\Session\Exception\RuntimeException', 'failed');
  474. $this->manager->start();
  475. }
  476. /**
  477. * @runInSeparateProcess
  478. */
  479. public function testSessionWriteCloseStoresMetadata()
  480. {
  481. $this->manager->start();
  482. $storage = $this->manager->getStorage();
  483. $storage->setMetadata('foo', 'bar');
  484. $metaData = $storage->getMetadata();
  485. $this->manager->writeClose();
  486. $this->assertSame($_SESSION['__ZF'], $metaData);
  487. }
  488. /**
  489. * @runInSeparateProcess
  490. */
  491. public function testSessionValidationDoesNotHaltOnNoopListener()
  492. {
  493. $validator = $this->getMock('stdClass', array('__invoke'));
  494. $validator->expects($this->once())->method('__invoke');
  495. $this->manager->getValidatorChain()->attach('session.validate', $validator);
  496. $this->assertTrue($this->manager->isValid());
  497. }
  498. }