bug #10720 [HttpFoundation] Fix DbalSessionHandler (Tobion)

This PR was merged into the 2.3 branch.

Discussion
----------

[HttpFoundation] Fix DbalSessionHandler

| Q             | A
| ------------- | ---
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets | -
| License       | MIT

This is basically the same as #10652 for the DbalSessionHandler.

- First commit fixes fix DbalSessionHandler for high concurrency, interface compliance, compatibility with all drivers (oci8, mysqli, pdo with mysql, sqlsrv, sqlite).
- Second commit updates phpdoc of SessionHandlerInterface and unifies parameters of all handlers according to interface (so inheritdoc actually makes sense).

Commits
-------

524bf84 [HttpFoundation] update phpdoc of SessionHandlerInterface and unify parameters of all handlers according to interface
ccdfbe6 [Doctrine Bridge] fix DbalSessionHandler for high concurrency, interface compliance, compatibility with all drivers (oci8, mysqli, pdo with mysql, sqlsrv, sqlite)
This commit is contained in:
Fabien Potencier 2014-04-18 22:55:02 +02:00
commit 5eacace321
9 changed files with 185 additions and 153 deletions

View File

@ -11,37 +11,64 @@
namespace Symfony\Bridge\Doctrine\HttpFoundation; namespace Symfony\Bridge\Doctrine\HttpFoundation;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Driver\Connection as DriverConnection;
use Doctrine\DBAL\Driver\Mysqli\MysqliConnection;
use Doctrine\DBAL\Driver\OCI8\OCI8Connection;
use Doctrine\DBAL\Driver\PDOConnection;
use Doctrine\DBAL\Driver\SQLSrv\SQLSrvConnection;
use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Driver\Connection; use Doctrine\DBAL\Platforms\OraclePlatform;
use Doctrine\DBAL\Platforms\SqlitePlatform;
use Doctrine\DBAL\Platforms\SQLServerPlatform;
/** /**
* DBAL based session storage. * DBAL based session storage.
* *
* This implementation is very similar to Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
* but uses the Doctrine driver connection interface and thus also works with non-PDO-based drivers like mysqli and OCI8.
*
* @author Fabien Potencier <fabien@symfony.com> * @author Fabien Potencier <fabien@symfony.com>
* @author Johannes M. Schmitt <schmittjoh@gmail.com> * @author Johannes M. Schmitt <schmittjoh@gmail.com>
* @author Tobias Schultze <http://tobion.de>
*/ */
class DbalSessionHandler implements \SessionHandlerInterface class DbalSessionHandler implements \SessionHandlerInterface
{ {
/** /**
* @var Connection * @var DriverConnection
*/ */
private $con; private $con;
/** /**
* @var string * @var string
*/ */
private $tableName; private $table;
/**
* @var string Column for session id
*/
private $idCol = 'sess_id';
/**
* @var string Column for session data
*/
private $dataCol = 'sess_data';
/**
* @var string Column for timestamp
*/
private $timeCol = 'sess_time';
/** /**
* Constructor. * Constructor.
* *
* @param Connection $con An instance of Connection. * @param DriverConnection $con A driver connection, preferably a wrapper Doctrine\DBAL\Connection for lazy connections
* @param string $tableName Table name. * @param string $tableName Table name
*/ */
public function __construct(Connection $con, $tableName = 'sessions') public function __construct(DriverConnection $con, $tableName = 'sessions')
{ {
$this->con = $con; $this->con = $con;
$this->tableName = $tableName; $this->table = $tableName;
} }
/** /**
@ -57,7 +84,6 @@ class DbalSessionHandler implements \SessionHandlerInterface
*/ */
public function close() public function close()
{ {
// do nothing
return true; return true;
} }
@ -66,12 +92,15 @@ class DbalSessionHandler implements \SessionHandlerInterface
*/ */
public function destroy($id) public function destroy($id)
{ {
// delete the record associated with this id
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
try { try {
$this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_id = :id", array( $stmt = $this->con->prepare($sql);
'id' => $id, $stmt->bindParam(':id', $id, \PDO::PARAM_STR);
)); $stmt->execute();
} catch (\PDOException $e) { } catch (\Exception $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); throw new \RuntimeException(sprintf('Exception was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e);
} }
return true; return true;
@ -82,12 +111,15 @@ class DbalSessionHandler implements \SessionHandlerInterface
*/ */
public function gc($lifetime) public function gc($lifetime)
{ {
// delete the session records that have expired
$sql = "DELETE FROM $this->table WHERE $this->timeCol < :time";
try { try {
$this->con->executeQuery("DELETE FROM {$this->tableName} WHERE sess_time < :time", array( $stmt = $this->con->prepare($sql);
'time' => time() - $lifetime, $stmt->bindValue(':time', time() - $lifetime, \PDO::PARAM_INT);
)); $stmt->execute();
} catch (\PDOException $e) { } catch (\Exception $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e); throw new \RuntimeException(sprintf('Exception was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e);
} }
return true; return true;
@ -98,21 +130,23 @@ class DbalSessionHandler implements \SessionHandlerInterface
*/ */
public function read($id) public function read($id)
{ {
try { $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id";
$data = $this->con->executeQuery("SELECT sess_data FROM {$this->tableName} WHERE sess_id = :id", array(
'id' => $id,
))->fetchColumn();
if (false !== $data) { try {
return base64_decode($data); $stmt = $this->con->prepare($sql);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
$stmt->execute();
// We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed
$sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM);
if ($sessionRows) {
return base64_decode($sessionRows[0][0]);
} }
// session does not exist, create it
$this->createNewSession($id);
return ''; return '';
} catch (\PDOException $e) { } catch (\Exception $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e); throw new \RuntimeException(sprintf('Exception was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
} }
} }
@ -121,54 +155,88 @@ class DbalSessionHandler implements \SessionHandlerInterface
*/ */
public function write($id, $data) public function write($id, $data)
{ {
$platform = $this->con->getDatabasePlatform(); // Session data can contain non binary safe characters so we need to encode it.
$encoded = base64_encode($data);
// this should maybe be abstracted in Doctrine DBAL // We use a MERGE SQL query when supported by the database.
if ($platform instanceof MySqlPlatform) { // Otherwise we have to use a transactional DELETE followed by INSERT to prevent duplicate entries under high concurrency.
$sql = "INSERT INTO {$this->tableName} (sess_id, sess_data, sess_time) VALUES (%1\$s, %2\$s, %3\$d) "
."ON DUPLICATE KEY UPDATE sess_data = VALUES(sess_data), sess_time = CASE WHEN sess_time = %3\$d THEN (VALUES(sess_time) + 1) ELSE VALUES(sess_time) END";
} else {
$sql = "UPDATE {$this->tableName} SET sess_data = %2\$s, sess_time = %3\$d WHERE sess_id = %1\$s";
}
try { try {
$rowCount = $this->con->exec(sprintf( $mergeSql = $this->getMergeSql();
$sql,
$this->con->quote($id),
//session data can contain non binary safe characters so we need to encode it
$this->con->quote(base64_encode($data)),
time()
));
if (!$rowCount) { if (null !== $mergeSql) {
// No session exists in the database to update. This happens when we have called $mergeStmt = $this->con->prepare($mergeSql);
// session_regenerate_id() $mergeStmt->bindParam(':id', $id, \PDO::PARAM_STR);
$this->createNewSession($id, $data); $mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$mergeStmt->execute();
return true;
} }
} catch (\PDOException $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e); $this->con->beginTransaction();
try {
$deleteStmt = $this->con->prepare(
"DELETE FROM $this->table WHERE $this->idCol = :id"
);
$deleteStmt->bindParam(':id', $id, \PDO::PARAM_STR);
$deleteStmt->execute();
$insertStmt = $this->con->prepare(
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"
);
$insertStmt->bindParam(':id', $id, \PDO::PARAM_STR);
$insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$insertStmt->execute();
$this->con->commit();
} catch (\Exception $e) {
$this->con->rollback();
throw $e;
}
} catch (\Exception $e) {
throw new \RuntimeException(sprintf('Exception was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
} }
return true; return true;
} }
/** /**
* Creates a new session with the given $id and $data * Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database.
* *
* @param string $id * @return string|null The SQL string or null when not supported
* @param string $data */
* private function getMergeSql()
* @return bool
*/
private function createNewSession($id, $data = '')
{ {
$this->con->exec(sprintf("INSERT INTO {$this->tableName} (sess_id, sess_data, sess_time) VALUES (%s, %s, %d)", $platform = $pdoDriver = null;
$this->con->quote($id),
//session data can contain non binary safe characters so we need to encode it
$this->con->quote(base64_encode($data)),
time()
));
return true; if ($this->con instanceof Connection) {
$platform = $this->con->getDatabasePlatform();
} elseif ($this->con instanceof PDOConnection) {
$pdoDriver = $this->con->getAttribute(\PDO::ATTR_DRIVER_NAME);
}
switch (true) {
case $this->con instanceof MysqliConnection || $platform instanceof MySqlPlatform || 'mysql' === $pdoDriver:
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) " .
"ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->timeCol = VALUES($this->timeCol)";
case $this->con instanceof OCI8Connection || $platform instanceof OraclePlatform || 'oci' === $pdoDriver:
// DUAL is Oracle specific dummy table
return "MERGE INTO $this->table USING DUAL ON ($this->idCol = :id) " .
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) " .
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data";
case $this->con instanceof SQLSrvConnection || $platform instanceof SQLServerPlatform || 'sqlsrv' === $pdoDriver:
// MS SQL Server requires MERGE be terminated by semicolon
return "MERGE INTO $this->table USING (SELECT 'x' AS dummy) AS src ON ($this->idCol = :id) " .
"WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) " .
"WHEN MATCHED THEN UPDATE SET $this->dataCol = :data;";
case $platform instanceof SqlitePlatform || 'sqlite' === $pdoDriver:
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)";
}
return null;
} }
} }

View File

@ -10,9 +10,7 @@
*/ */
/** /**
* SessionHandlerInterface * SessionHandlerInterface for PHP < 5.4
*
* Provides forward compatibility with PHP 5.4
* *
* Extensive documentation can be found at php.net, see links: * Extensive documentation can be found at php.net, see links:
* *
@ -25,76 +23,68 @@
interface SessionHandlerInterface interface SessionHandlerInterface
{ {
/** /**
* Open session. * Re-initializes existing session, or creates a new one.
* *
* @see http://php.net/sessionhandlerinterface.open * @see http://php.net/sessionhandlerinterface.open
* *
* @param string $savePath Save path. * @param string $savePath Save path
* @param string $sessionName Session Name. * @param string $sessionName Session name, see http://php.net/function.session-name.php
* *
* @throws \RuntimeException If something goes wrong starting the session. * @return bool true on success, false on failure
*
* @return bool
*/ */
public function open($savePath, $sessionName); public function open($savePath, $sessionName);
/** /**
* Close session. * Closes the current session.
* *
* @see http://php.net/sessionhandlerinterface.close * @see http://php.net/sessionhandlerinterface.close
* *
* @return bool * @return bool true on success, false on failure
*/ */
public function close(); public function close();
/** /**
* Read session. * Reads the session data.
*
* @param string $sessionId
* *
* @see http://php.net/sessionhandlerinterface.read * @see http://php.net/sessionhandlerinterface.read
* *
* @throws \RuntimeException On fatal error but not "record not found". * @param string $sessionId Session ID, see http://php.net/function.session-id
* *
* @return string String as stored in persistent storage or empty string in all other cases. * @return string Same session data as passed in write() or empty string when non-existent or on failure
*/ */
public function read($sessionId); public function read($sessionId);
/** /**
* Commit session to storage. * Writes the session data to the storage.
* *
* @see http://php.net/sessionhandlerinterface.write * @see http://php.net/sessionhandlerinterface.write
* *
* @param string $sessionId Session ID. * @param string $sessionId Session ID , see http://php.net/function.session-id
* @param string $data Session serialized data to save. * @param string $data Serialized session data to save
* *
* @return bool * @return bool true on success, false on failure
*/ */
public function write($sessionId, $data); public function write($sessionId, $data);
/** /**
* Destroys this session. * Destroys a session.
* *
* @see http://php.net/sessionhandlerinterface.destroy * @see http://php.net/sessionhandlerinterface.destroy
* *
* @param string $sessionId Session ID. * @param string $sessionId Session ID, see http://php.net/function.session-id
* *
* @throws \RuntimeException On fatal error. * @return bool true on success, false on failure
*
* @return bool
*/ */
public function destroy($sessionId); public function destroy($sessionId);
/** /**
* Garbage collection for storage. * Cleans up expired sessions (garbage collection).
* *
* @see http://php.net/sessionhandlerinterface.gc * @see http://php.net/sessionhandlerinterface.gc
* *
* @param int $lifetime Max lifetime in seconds to keep sessions stored. * @param string|int $maxlifetime Sessions that have not updated for the last maxlifetime seconds will be removed
* *
* @throws \RuntimeException On fatal error. * @return bool true on success, false on failure
*
* @return bool
*/ */
public function gc($lifetime); public function gc($maxlifetime);
} }

View File

@ -101,7 +101,7 @@ class MemcacheSessionHandler implements \SessionHandlerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function gc($lifetime) public function gc($maxlifetime)
{ {
// not required here because memcache will auto expire the records anyhow. // not required here because memcache will auto expire the records anyhow.
return true; return true;

View File

@ -107,7 +107,7 @@ class MemcachedSessionHandler implements \SessionHandlerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function gc($lifetime) public function gc($maxlifetime)
{ {
// not required here because memcached will auto expire the records anyhow. // not required here because memcached will auto expire the records anyhow.
return true; return true;

View File

@ -99,7 +99,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function gc($lifetime) public function gc($maxlifetime)
{ {
/* Note: MongoDB 2.2+ supports TTL collections, which may be used in /* Note: MongoDB 2.2+ supports TTL collections, which may be used in
* place of this method by indexing the "time_field" field with an * place of this method by indexing the "time_field" field with an
@ -109,7 +109,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
* *
* See: http://docs.mongodb.org/manual/tutorial/expire-data/ * See: http://docs.mongodb.org/manual/tutorial/expire-data/
*/ */
$time = new \MongoDate(time() - $lifetime); $time = new \MongoDate(time() - $maxlifetime);
$this->getCollection()->remove(array( $this->getCollection()->remove(array(
$this->options['time_field'] => array('$lt' => $time), $this->options['time_field'] => array('$lt' => $time),

View File

@ -65,7 +65,7 @@ class NullSessionHandler implements \SessionHandlerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function gc($lifetime) public function gc($maxlifetime)
{ {
return true; return true;
} }

View File

@ -83,7 +83,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function open($path, $name) public function open($savePath, $sessionName)
{ {
return true; return true;
} }
@ -99,14 +99,14 @@ class PdoSessionHandler implements \SessionHandlerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function destroy($id) public function destroy($sessionId)
{ {
// delete the record associated with this id // delete the record associated with this id
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id"; $sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
try { try {
$stmt = $this->pdo->prepare($sql); $stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$stmt->execute(); $stmt->execute();
} catch (\PDOException $e) { } catch (\PDOException $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e); throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e);
@ -118,14 +118,14 @@ class PdoSessionHandler implements \SessionHandlerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function gc($lifetime) public function gc($maxlifetime)
{ {
// delete the session records that have expired // delete the session records that have expired
$sql = "DELETE FROM $this->table WHERE $this->timeCol < :time"; $sql = "DELETE FROM $this->table WHERE $this->timeCol < :time";
try { try {
$stmt = $this->pdo->prepare($sql); $stmt = $this->pdo->prepare($sql);
$stmt->bindValue(':time', time() - $lifetime, \PDO::PARAM_INT); $stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT);
$stmt->execute(); $stmt->execute();
} catch (\PDOException $e) { } catch (\PDOException $e) {
throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e); throw new \RuntimeException(sprintf('PDOException was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e);
@ -137,13 +137,13 @@ class PdoSessionHandler implements \SessionHandlerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function read($id) public function read($sessionId)
{ {
$sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id"; $sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id";
try { try {
$stmt = $this->pdo->prepare($sql); $stmt = $this->pdo->prepare($sql);
$stmt->bindParam(':id', $id, \PDO::PARAM_STR); $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$stmt->execute(); $stmt->execute();
// We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed // We use fetchAll instead of fetchColumn to make sure the DB cursor gets closed
@ -162,7 +162,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function write($id, $data) public function write($sessionId, $data)
{ {
// Session data can contain non binary safe characters so we need to encode it. // Session data can contain non binary safe characters so we need to encode it.
$encoded = base64_encode($data); $encoded = base64_encode($data);
@ -175,7 +175,7 @@ class PdoSessionHandler implements \SessionHandlerInterface
if (null !== $mergeSql) { if (null !== $mergeSql) {
$mergeStmt = $this->pdo->prepare($mergeSql); $mergeStmt = $this->pdo->prepare($mergeSql);
$mergeStmt->bindParam(':id', $id, \PDO::PARAM_STR); $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); $mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT); $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$mergeStmt->execute(); $mergeStmt->execute();
@ -189,13 +189,13 @@ class PdoSessionHandler implements \SessionHandlerInterface
$deleteStmt = $this->pdo->prepare( $deleteStmt = $this->pdo->prepare(
"DELETE FROM $this->table WHERE $this->idCol = :id" "DELETE FROM $this->table WHERE $this->idCol = :id"
); );
$deleteStmt->bindParam(':id', $id, \PDO::PARAM_STR); $deleteStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$deleteStmt->execute(); $deleteStmt->execute();
$insertStmt = $this->pdo->prepare( $insertStmt = $this->pdo->prepare(
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)" "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"
); );
$insertStmt->bindParam(':id', $id, \PDO::PARAM_STR); $insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
$insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR); $insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT); $insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
$insertStmt->execute(); $insertStmt->execute();

View File

@ -64,25 +64,25 @@ class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterf
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function read($id) public function read($sessionId)
{ {
return (string) $this->handler->read($id); return (string) $this->handler->read($sessionId);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function write($id, $data) public function write($sessionId, $data)
{ {
return (bool) $this->handler->write($id, $data); return (bool) $this->handler->write($sessionId, $data);
} }
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function destroy($id) public function destroy($sessionId)
{ {
return (bool) $this->handler->destroy($id); return (bool) $this->handler->destroy($sessionId);
} }
/** /**

View File

@ -11,10 +11,11 @@
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage; namespace Symfony\Component\HttpFoundation\Tests\Session\Storage;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag; use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NativeSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler;
use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy;
use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy; use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
@ -175,9 +176,9 @@ class NativeSessionStorageTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new NativeSessionHandler()); $storage->setSaveHandler(new NativeSessionHandler());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new SessionHandlerProxy(new SessionHandler())); $storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new SessionHandler()); $storage->setSaveHandler(new NullSessionHandler());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new NativeProxy()); $storage->setSaveHandler(new NativeProxy());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\NativeProxy', $storage->getSaveHandler());
@ -199,9 +200,9 @@ class NativeSessionStorageTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new NativeSessionHandler()); $storage->setSaveHandler(new NativeSessionHandler());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new SessionHandlerProxy(new SessionHandler())); $storage->setSaveHandler(new SessionHandlerProxy(new NullSessionHandler()));
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
$storage->setSaveHandler(new SessionHandler()); $storage->setSaveHandler(new NullSessionHandler());
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler()); $this->assertInstanceOf('Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy', $storage->getSaveHandler());
} }
@ -254,30 +255,3 @@ class NativeSessionStorageTest extends \PHPUnit_Framework_TestCase
$storage->start(); $storage->start();
} }
} }
class SessionHandler implements \SessionHandlerInterface
{
public function open($savePath, $sessionName)
{
}
public function close()
{
}
public function read($id)
{
}
public function write($id, $data)
{
}
public function destroy($id)
{
}
public function gc($maxlifetime)
{
}
}