[HttpFoundation] Fix PdoSessionHandler to work properly with streams

This commit is contained in:
Eugene Leonovich 2014-10-06 00:36:25 +02:00 committed by Fabien Potencier
parent a7d52fda77
commit 9531a2b3f1
2 changed files with 100 additions and 3 deletions

View File

@ -521,11 +521,11 @@ class PdoSessionHandler implements \SessionHandlerInterface
return '';
}
return $sessionRows[0][0];
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
}
if (self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
// Exlusive-reading of non-existent rows does not block, so we need to do an insert to block
// Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
// until other connections to the session are committed.
try {
$insertStmt = $this->pdo->prepare(
@ -546,7 +546,11 @@ class PdoSessionHandler implements \SessionHandlerInterface
$selectStmt->execute();
$sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
return $sessionRows ? $sessionRows[0][0] : '';
if ($sessionRows) {
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
}
return '';
}
throw $e;

View File

@ -135,6 +135,53 @@ class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertSame($sessionData, $readData, 'Written value can be read back correctly');
}
public function testReadConvertsStreamToString()
{
$pdo = new MockPdo('pgsql');
$pdo->prepareResult = $this->getMock('PDOStatement');
$content = 'foobar';
$stream = $this->createStream($content);
$pdo->prepareResult->expects($this->once())->method('fetchAll')
->will($this->returnValue(array(array($stream, 42, time()))));
$storage = new PdoSessionHandler($pdo);
$result = $storage->read('foo');
$this->assertSame($content, $result);
}
public function testReadLockedConvertsStreamToString()
{
$pdo = new MockPdo('pgsql');
$selectStmt = $this->getMock('PDOStatement');
$insertStmt = $this->getMock('PDOStatement');
$pdo->prepareResult = function ($statement) use ($selectStmt, $insertStmt) {
return 0 === strpos($statement, 'INSERT') ? $insertStmt : $selectStmt;
};
$content = 'foobar';
$stream = $this->createStream($content);
$exception = null;
$selectStmt->expects($this->atLeast(2))->method('fetchAll')
->will($this->returnCallback(function () use (&$exception, $stream) {
return $exception ? array(array($stream, 42, time())) : array();
}));
$insertStmt->expects($this->once())->method('execute')
->will($this->returnCallback(function () use (&$exception) {
throw $exception = new \PDOException('', '23');
}));
$storage = new PdoSessionHandler($pdo);
$result = $storage->read('foo');
$this->assertSame($content, $result);
}
public function testReadingRequiresExactlySameId()
{
$storage = new PdoSessionHandler($this->getMemorySqlitePdo());
@ -263,4 +310,50 @@ class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('\PDO', $method->invoke($storage));
}
private function createStream($content)
{
$stream = tmpfile();
fwrite($stream, $content);
fseek($stream, 0);
return $stream;
}
}
class MockPdo extends \PDO
{
public $prepareResult;
private $driverName;
private $errorMode;
public function __construct($driverName = null, $errorMode = null)
{
$this->driverName = $driverName;
$this->errorMode = null !== $errorMode ?: \PDO::ERRMODE_EXCEPTION;
}
public function getAttribute($attribute)
{
if (\PDO::ATTR_ERRMODE === $attribute) {
return $this->errorMode;
}
if (\PDO::ATTR_DRIVER_NAME === $attribute) {
return $this->driverName;
}
return parent::getAttribute($attribute);
}
public function prepare($statement, $driverOptions = array())
{
return is_callable($this->prepareResult)
? call_user_func($this->prepareResult, $statement, $driverOptions)
: $this->prepareResult;
}
public function beginTransaction()
{
}
}