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