Yaffs site version 1.1
[yaffs-website] / vendor / symfony / http-foundation / Tests / Session / Storage / Handler / PdoSessionHandlerTest.php
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
12 namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
13
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler;
16
17 /**
18  * @requires extension pdo_sqlite
19  * @group time-sensitive
20  */
21 class PdoSessionHandlerTest extends TestCase
22 {
23     private $dbFile;
24
25     protected function tearDown()
26     {
27         // make sure the temporary database file is deleted when it has been created (even when a test fails)
28         if ($this->dbFile) {
29             @unlink($this->dbFile);
30         }
31         parent::tearDown();
32     }
33
34     protected function getPersistentSqliteDsn()
35     {
36         $this->dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions');
37
38         return 'sqlite:'.$this->dbFile;
39     }
40
41     protected function getMemorySqlitePdo()
42     {
43         $pdo = new \PDO('sqlite::memory:');
44         $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
45         $storage = new PdoSessionHandler($pdo);
46         $storage->createTable();
47
48         return $pdo;
49     }
50
51     /**
52      * @expectedException \InvalidArgumentException
53      */
54     public function testWrongPdoErrMode()
55     {
56         $pdo = $this->getMemorySqlitePdo();
57         $pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_SILENT);
58
59         $storage = new PdoSessionHandler($pdo);
60     }
61
62     /**
63      * @expectedException \RuntimeException
64      */
65     public function testInexistentTable()
66     {
67         $storage = new PdoSessionHandler($this->getMemorySqlitePdo(), array('db_table' => 'inexistent_table'));
68         $storage->open('', 'sid');
69         $storage->read('id');
70         $storage->write('id', 'data');
71         $storage->close();
72     }
73
74     /**
75      * @expectedException \RuntimeException
76      */
77     public function testCreateTableTwice()
78     {
79         $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
80         $storage->createTable();
81     }
82
83     public function testWithLazyDsnConnection()
84     {
85         $dsn = $this->getPersistentSqliteDsn();
86
87         $storage = new PdoSessionHandler($dsn);
88         $storage->createTable();
89         $storage->open('', 'sid');
90         $data = $storage->read('id');
91         $storage->write('id', 'data');
92         $storage->close();
93         $this->assertSame('', $data, 'New session returns empty string data');
94
95         $storage->open('', 'sid');
96         $data = $storage->read('id');
97         $storage->close();
98         $this->assertSame('data', $data, 'Written value can be read back correctly');
99     }
100
101     public function testWithLazySavePathConnection()
102     {
103         $dsn = $this->getPersistentSqliteDsn();
104
105         // Open is called with what ini_set('session.save_path', $dsn) would mean
106         $storage = new PdoSessionHandler(null);
107         $storage->open($dsn, 'sid');
108         $storage->createTable();
109         $data = $storage->read('id');
110         $storage->write('id', 'data');
111         $storage->close();
112         $this->assertSame('', $data, 'New session returns empty string data');
113
114         $storage->open($dsn, 'sid');
115         $data = $storage->read('id');
116         $storage->close();
117         $this->assertSame('data', $data, 'Written value can be read back correctly');
118     }
119
120     public function testReadWriteReadWithNullByte()
121     {
122         $sessionData = 'da'."\0".'ta';
123
124         $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
125         $storage->open('', 'sid');
126         $readData = $storage->read('id');
127         $storage->write('id', $sessionData);
128         $storage->close();
129         $this->assertSame('', $readData, 'New session returns empty string data');
130
131         $storage->open('', 'sid');
132         $readData = $storage->read('id');
133         $storage->close();
134         $this->assertSame($sessionData, $readData, 'Written value can be read back correctly');
135     }
136
137     public function testReadConvertsStreamToString()
138     {
139         if (defined('HHVM_VERSION')) {
140             $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
141         }
142
143         $pdo = new MockPdo('pgsql');
144         $pdo->prepareResult = $this->getMockBuilder('PDOStatement')->getMock();
145
146         $content = 'foobar';
147         $stream = $this->createStream($content);
148
149         $pdo->prepareResult->expects($this->once())->method('fetchAll')
150             ->will($this->returnValue(array(array($stream, 42, time()))));
151
152         $storage = new PdoSessionHandler($pdo);
153         $result = $storage->read('foo');
154
155         $this->assertSame($content, $result);
156     }
157
158     public function testReadLockedConvertsStreamToString()
159     {
160         if (defined('HHVM_VERSION')) {
161             $this->markTestSkipped('PHPUnit_MockObject cannot mock the PDOStatement class on HHVM. See https://github.com/sebastianbergmann/phpunit-mock-objects/pull/289');
162         }
163
164         $pdo = new MockPdo('pgsql');
165         $selectStmt = $this->getMockBuilder('PDOStatement')->getMock();
166         $insertStmt = $this->getMockBuilder('PDOStatement')->getMock();
167
168         $pdo->prepareResult = function ($statement) use ($selectStmt, $insertStmt) {
169             return 0 === strpos($statement, 'INSERT') ? $insertStmt : $selectStmt;
170         };
171
172         $content = 'foobar';
173         $stream = $this->createStream($content);
174         $exception = null;
175
176         $selectStmt->expects($this->atLeast(2))->method('fetchAll')
177             ->will($this->returnCallback(function () use (&$exception, $stream) {
178                 return $exception ? array(array($stream, 42, time())) : array();
179             }));
180
181         $insertStmt->expects($this->once())->method('execute')
182             ->will($this->returnCallback(function () use (&$exception) {
183                 throw $exception = new \PDOException('', '23');
184             }));
185
186         $storage = new PdoSessionHandler($pdo);
187         $result = $storage->read('foo');
188
189         $this->assertSame($content, $result);
190     }
191
192     public function testReadingRequiresExactlySameId()
193     {
194         $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
195         $storage->open('', 'sid');
196         $storage->write('id', 'data');
197         $storage->write('test', 'data');
198         $storage->write('space ', 'data');
199         $storage->close();
200
201         $storage->open('', 'sid');
202         $readDataCaseSensitive = $storage->read('ID');
203         $readDataNoCharFolding = $storage->read('tést');
204         $readDataKeepSpace = $storage->read('space ');
205         $readDataExtraSpace = $storage->read('space  ');
206         $storage->close();
207
208         $this->assertSame('', $readDataCaseSensitive, 'Retrieval by ID should be case-sensitive (collation setting)');
209         $this->assertSame('', $readDataNoCharFolding, 'Retrieval by ID should not do character folding (collation setting)');
210         $this->assertSame('data', $readDataKeepSpace, 'Retrieval by ID requires spaces as-is');
211         $this->assertSame('', $readDataExtraSpace, 'Retrieval by ID requires spaces as-is');
212     }
213
214     /**
215      * Simulates session_regenerate_id(true) which will require an INSERT or UPDATE (replace).
216      */
217     public function testWriteDifferentSessionIdThanRead()
218     {
219         $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
220         $storage->open('', 'sid');
221         $storage->read('id');
222         $storage->destroy('id');
223         $storage->write('new_id', 'data_of_new_session_id');
224         $storage->close();
225
226         $storage->open('', 'sid');
227         $data = $storage->read('new_id');
228         $storage->close();
229
230         $this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available');
231     }
232
233     public function testWrongUsageStillWorks()
234     {
235         // wrong method sequence that should no happen, but still works
236         $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
237         $storage->write('id', 'data');
238         $storage->write('other_id', 'other_data');
239         $storage->destroy('inexistent');
240         $storage->open('', 'sid');
241         $data = $storage->read('id');
242         $otherData = $storage->read('other_id');
243         $storage->close();
244
245         $this->assertSame('data', $data);
246         $this->assertSame('other_data', $otherData);
247     }
248
249     public function testSessionDestroy()
250     {
251         $pdo = $this->getMemorySqlitePdo();
252         $storage = new PdoSessionHandler($pdo);
253
254         $storage->open('', 'sid');
255         $storage->read('id');
256         $storage->write('id', 'data');
257         $storage->close();
258         $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
259
260         $storage->open('', 'sid');
261         $storage->read('id');
262         $storage->destroy('id');
263         $storage->close();
264         $this->assertEquals(0, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
265
266         $storage->open('', 'sid');
267         $data = $storage->read('id');
268         $storage->close();
269         $this->assertSame('', $data, 'Destroyed session returns empty string');
270     }
271
272     public function testSessionGC()
273     {
274         $previousLifeTime = ini_set('session.gc_maxlifetime', 1000);
275         $pdo = $this->getMemorySqlitePdo();
276         $storage = new PdoSessionHandler($pdo);
277
278         $storage->open('', 'sid');
279         $storage->read('id');
280         $storage->write('id', 'data');
281         $storage->close();
282
283         $storage->open('', 'sid');
284         $storage->read('gc_id');
285         ini_set('session.gc_maxlifetime', -1); // test that you can set lifetime of a session after it has been read
286         $storage->write('gc_id', 'data');
287         $storage->close();
288         $this->assertEquals(2, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'No session pruned because gc not called');
289
290         $storage->open('', 'sid');
291         $data = $storage->read('gc_id');
292         $storage->gc(-1);
293         $storage->close();
294
295         ini_set('session.gc_maxlifetime', $previousLifeTime);
296
297         $this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet');
298         $this->assertEquals(1, $pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn(), 'Expired session is pruned');
299     }
300
301     public function testGetConnection()
302     {
303         $storage = new PdoSessionHandler($this->getMemorySqlitePdo());
304
305         $method = new \ReflectionMethod($storage, 'getConnection');
306         $method->setAccessible(true);
307
308         $this->assertInstanceOf('\PDO', $method->invoke($storage));
309     }
310
311     public function testGetConnectionConnectsIfNeeded()
312     {
313         $storage = new PdoSessionHandler('sqlite::memory:');
314
315         $method = new \ReflectionMethod($storage, 'getConnection');
316         $method->setAccessible(true);
317
318         $this->assertInstanceOf('\PDO', $method->invoke($storage));
319     }
320
321     private function createStream($content)
322     {
323         $stream = tmpfile();
324         fwrite($stream, $content);
325         fseek($stream, 0);
326
327         return $stream;
328     }
329 }
330
331 class MockPdo extends \PDO
332 {
333     public $prepareResult;
334     private $driverName;
335     private $errorMode;
336
337     public function __construct($driverName = null, $errorMode = null)
338     {
339         $this->driverName = $driverName;
340         $this->errorMode = null !== $errorMode ?: \PDO::ERRMODE_EXCEPTION;
341     }
342
343     public function getAttribute($attribute)
344     {
345         if (\PDO::ATTR_ERRMODE === $attribute) {
346             return $this->errorMode;
347         }
348
349         if (\PDO::ATTR_DRIVER_NAME === $attribute) {
350             return $this->driverName;
351         }
352
353         return parent::getAttribute($attribute);
354     }
355
356     public function prepare($statement, $driverOptions = array())
357     {
358         return is_callable($this->prepareResult)
359             ? call_user_func($this->prepareResult, $statement, $driverOptions)
360             : $this->prepareResult;
361     }
362
363     public function beginTransaction()
364     {
365     }
366
367     public function rollBack()
368     {
369     }
370 }