NativeSessionStorageTest.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
  13. use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
  14. use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeFileSessionHandler;
  15. use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
  16. use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
  17. use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
  18. /**
  19. * Test class for NativeSessionStorage.
  20. *
  21. * @author Drak <drak@zikula.org>
  22. *
  23. * These tests require separate processes.
  24. *
  25. * @runTestsInSeparateProcesses
  26. *
  27. * @preserveGlobalState disabled
  28. */
  29. class NativeSessionStorageTest extends TestCase
  30. {
  31. private $savePath;
  32. protected function setUp(): void
  33. {
  34. $this->iniSet('session.save_handler', 'files');
  35. $this->iniSet('session.save_path', $this->savePath = sys_get_temp_dir().'/sftest');
  36. if (!is_dir($this->savePath)) {
  37. mkdir($this->savePath);
  38. }
  39. }
  40. protected function tearDown(): void
  41. {
  42. session_write_close();
  43. array_map('unlink', glob($this->savePath.'/*'));
  44. if (is_dir($this->savePath)) {
  45. @rmdir($this->savePath);
  46. }
  47. $this->savePath = null;
  48. }
  49. protected function getStorage(array $options = []): NativeSessionStorage
  50. {
  51. $storage = new NativeSessionStorage($options);
  52. $storage->registerBag(new AttributeBag());
  53. return $storage;
  54. }
  55. public function testBag()
  56. {
  57. $storage = $this->getStorage();
  58. $bag = new FlashBag();
  59. $storage->registerBag($bag);
  60. $this->assertSame($bag, $storage->getBag($bag->getName()));
  61. }
  62. public function testRegisterBagException()
  63. {
  64. $this->expectException(\InvalidArgumentException::class);
  65. $storage = $this->getStorage();
  66. $storage->getBag('non_existing');
  67. }
  68. public function testRegisterBagForAStartedSessionThrowsException()
  69. {
  70. $this->expectException(\LogicException::class);
  71. $storage = $this->getStorage();
  72. $storage->start();
  73. $storage->registerBag(new AttributeBag());
  74. }
  75. public function testGetId()
  76. {
  77. $storage = $this->getStorage();
  78. $this->assertSame('', $storage->getId(), 'Empty ID before starting session');
  79. $storage->start();
  80. $id = $storage->getId();
  81. $this->assertIsString($id);
  82. $this->assertNotSame('', $id);
  83. $storage->save();
  84. $this->assertSame($id, $storage->getId(), 'ID stays after saving session');
  85. }
  86. public function testRegenerate()
  87. {
  88. $storage = $this->getStorage();
  89. $storage->start();
  90. $id = $storage->getId();
  91. $storage->getBag('attributes')->set('lucky', 7);
  92. $storage->regenerate();
  93. $this->assertNotEquals($id, $storage->getId());
  94. $this->assertEquals(7, $storage->getBag('attributes')->get('lucky'));
  95. }
  96. public function testRegenerateDestroy()
  97. {
  98. $storage = $this->getStorage();
  99. $storage->start();
  100. $id = $storage->getId();
  101. $storage->getBag('attributes')->set('legs', 11);
  102. $storage->regenerate(true);
  103. $this->assertNotEquals($id, $storage->getId());
  104. $this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
  105. }
  106. public function testRegenerateWithCustomLifetime()
  107. {
  108. $storage = $this->getStorage();
  109. $storage->start();
  110. $id = $storage->getId();
  111. $lifetime = 999999;
  112. $storage->getBag('attributes')->set('legs', 11);
  113. $storage->regenerate(false, $lifetime);
  114. $this->assertNotEquals($id, $storage->getId());
  115. $this->assertEquals(11, $storage->getBag('attributes')->get('legs'));
  116. $this->assertEquals($lifetime, \ini_get('session.cookie_lifetime'));
  117. }
  118. public function testSessionGlobalIsUpToDateAfterIdRegeneration()
  119. {
  120. $storage = $this->getStorage();
  121. $storage->start();
  122. $storage->getBag('attributes')->set('lucky', 7);
  123. $storage->regenerate();
  124. $storage->getBag('attributes')->set('lucky', 42);
  125. $this->assertEquals(42, $_SESSION['_sf2_attributes']['lucky']);
  126. }
  127. public function testRegenerationFailureDoesNotFlagStorageAsStarted()
  128. {
  129. $storage = $this->getStorage();
  130. $this->assertFalse($storage->regenerate());
  131. $this->assertFalse($storage->isStarted());
  132. }
  133. public function testDefaultSessionCacheLimiter()
  134. {
  135. $this->iniSet('session.cache_limiter', 'nocache');
  136. new NativeSessionStorage();
  137. $this->assertEquals('', \ini_get('session.cache_limiter'));
  138. }
  139. public function testExplicitSessionCacheLimiter()
  140. {
  141. $this->iniSet('session.cache_limiter', 'nocache');
  142. new NativeSessionStorage(['cache_limiter' => 'public']);
  143. $this->assertEquals('public', \ini_get('session.cache_limiter'));
  144. }
  145. public function testCookieOptions()
  146. {
  147. $options = [
  148. 'cookie_lifetime' => 123456,
  149. 'cookie_path' => '/my/cookie/path',
  150. 'cookie_domain' => 'symfony.example.com',
  151. 'cookie_secure' => true,
  152. 'cookie_httponly' => false,
  153. ];
  154. if (\PHP_VERSION_ID >= 70300) {
  155. $options['cookie_samesite'] = 'lax';
  156. }
  157. $this->getStorage($options);
  158. $temp = session_get_cookie_params();
  159. $gco = [];
  160. foreach ($temp as $key => $value) {
  161. $gco['cookie_'.$key] = $value;
  162. }
  163. $this->assertEquals($options, $gco);
  164. }
  165. public function testSessionOptions()
  166. {
  167. $options = [
  168. 'trans_sid_tags' => 'a=href',
  169. 'cache_expire' => '200',
  170. ];
  171. $this->getStorage($options);
  172. $this->assertSame('a=href', \ini_get('session.trans_sid_tags'));
  173. $this->assertSame('200', \ini_get('session.cache_expire'));
  174. }
  175. public function testSetSaveHandler()
  176. {
  177. $this->iniSet('session.save_handler', 'files');
  178. $storage = $this->getStorage();
  179. $storage->setSaveHandler();
  180. $this->assertInstanceOf(SessionHandlerProxy::class, $storage->getSaveHandler());
  181. $storage->setSaveHandler(null);
  182. $this->assertInstanceOf(SessionHandlerProxy::class, $storage->getSaveHandler());
  183. $storage->setSaveHandler(new SessionHandlerProxy(new NativeFileSessionHandler()));
  184. $this->assertInstanceOf(SessionHandlerProxy::class, $storage->getSaveHandler());
  185. $storage->setSaveHandler(new NativeFileSessionHandler());
  186. $this->assertInstanceOf(SessionHandlerProxy::class, $storage->getSaveHandler());
  187. $storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
  188. $this->assertInstanceOf(SessionHandlerProxy::class, $storage->getSaveHandler());
  189. $storage->setSaveHandler(new NullSessionHandler());
  190. $this->assertInstanceOf(SessionHandlerProxy::class, $storage->getSaveHandler());
  191. }
  192. public function testStarted()
  193. {
  194. $this->expectException(\RuntimeException::class);
  195. $storage = $this->getStorage();
  196. $this->assertFalse($storage->getSaveHandler()->isActive());
  197. $this->assertFalse($storage->isStarted());
  198. session_start();
  199. $this->assertTrue(isset($_SESSION));
  200. $this->assertTrue($storage->getSaveHandler()->isActive());
  201. // PHP session might have started, but the storage driver has not, so false is correct here
  202. $this->assertFalse($storage->isStarted());
  203. $key = $storage->getMetadataBag()->getStorageKey();
  204. $this->assertArrayNotHasKey($key, $_SESSION);
  205. $storage->start();
  206. }
  207. public function testRestart()
  208. {
  209. $storage = $this->getStorage();
  210. $storage->start();
  211. $id = $storage->getId();
  212. $storage->getBag('attributes')->set('lucky', 7);
  213. $storage->save();
  214. $storage->start();
  215. $this->assertSame($id, $storage->getId(), 'Same session ID after restarting');
  216. $this->assertSame(7, $storage->getBag('attributes')->get('lucky'), 'Data still available');
  217. }
  218. public function testCanCreateNativeSessionStorageWhenSessionAlreadyStarted()
  219. {
  220. session_start();
  221. $this->getStorage();
  222. // Assert no exception has been thrown by `getStorage()`
  223. $this->addToAssertionCount(1);
  224. }
  225. public function testSetSessionOptionsOnceSessionStartedIsIgnored()
  226. {
  227. session_start();
  228. $this->getStorage([
  229. 'name' => 'something-else',
  230. ]);
  231. // Assert no exception has been thrown by `getStorage()`
  232. $this->addToAssertionCount(1);
  233. }
  234. public function testGetBagsOnceSessionStartedIsIgnored()
  235. {
  236. session_start();
  237. $bag = new AttributeBag();
  238. $bag->setName('flashes');
  239. $storage = $this->getStorage();
  240. $storage->registerBag($bag);
  241. $this->assertEquals($storage->getBag('flashes'), $bag);
  242. }
  243. public function testRegenerateInvalidSessionIdForNativeFileSessionHandler()
  244. {
  245. $_COOKIE[session_name()] = '&~[';
  246. session_id('&~[');
  247. $storage = new NativeSessionStorage([], new NativeFileSessionHandler());
  248. $started = $storage->start();
  249. $this->assertTrue($started);
  250. $this->assertMatchesRegularExpression('/^[a-zA-Z0-9,-]{22,250}$/', session_id());
  251. $storage->save();
  252. $_COOKIE[session_name()] = '&~[';
  253. session_id('&~[');
  254. $storage = new NativeSessionStorage([], new SessionHandlerProxy(new NativeFileSessionHandler()));
  255. $started = $storage->start();
  256. $this->assertTrue($started);
  257. $this->assertMatchesRegularExpression('/^[a-zA-Z0-9,-]{22,250}$/', session_id());
  258. $storage->save();
  259. $_COOKIE[session_name()] = '&~[';
  260. session_id('&~[');
  261. $storage = new NativeSessionStorage([], new NullSessionHandler());
  262. $started = $storage->start();
  263. $this->assertTrue($started);
  264. $this->assertSame('&~[', session_id());
  265. }
  266. public function testSaveHandlesNullSessionGracefully()
  267. {
  268. $storage = $this->getStorage();
  269. $_SESSION = null;
  270. $storage->save();
  271. $this->addToAssertionCount(1);
  272. }
  273. }