[Session] remove deprecated session handlers
This commit is contained in:
parent
0958fc442f
commit
0a7af3fb50
@ -1,276 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\HttpFoundation;
|
||||
|
||||
@trigger_error(sprintf('The class %s is deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler instead.', DbalSessionHandler::class), E_USER_DEPRECATED);
|
||||
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Doctrine\DBAL\Driver\DriverException;
|
||||
use Doctrine\DBAL\Driver\ServerInfoAwareConnection;
|
||||
use Doctrine\DBAL\Platforms\SQLServer2008Platform;
|
||||
|
||||
/**
|
||||
* DBAL based session storage.
|
||||
*
|
||||
* This implementation is very similar to Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler
|
||||
* but uses a Doctrine connection and thus also works with non-PDO-based drivers like mysqli and OCI8.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
* @author Tobias Schultze <http://tobion.de>
|
||||
*
|
||||
* @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler instead.
|
||||
*/
|
||||
class DbalSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var Connection
|
||||
*/
|
||||
private $con;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
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';
|
||||
|
||||
/**
|
||||
* @param Connection $con A connection
|
||||
* @param string $tableName Table name
|
||||
*/
|
||||
public function __construct(Connection $con, $tableName = 'sessions')
|
||||
{
|
||||
$this->con = $con;
|
||||
$this->table = $tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
// delete the record associated with this id
|
||||
$sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
|
||||
|
||||
try {
|
||||
$stmt = $this->con->prepare($sql);
|
||||
$stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$stmt->execute();
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException(sprintf('Exception was thrown when trying to delete a session: %s', $e->getMessage()), 0, $e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
// delete the session records that have expired
|
||||
$sql = "DELETE FROM $this->table WHERE $this->timeCol < :time";
|
||||
|
||||
try {
|
||||
$stmt = $this->con->prepare($sql);
|
||||
$stmt->bindValue(':time', time() - $maxlifetime, \PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException(sprintf('Exception was thrown when trying to delete expired sessions: %s', $e->getMessage()), 0, $e);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
$sql = "SELECT $this->dataCol FROM $this->table WHERE $this->idCol = :id";
|
||||
|
||||
try {
|
||||
$stmt = $this->con->prepare($sql);
|
||||
$stmt->bindParam(':id', $sessionId, \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]);
|
||||
}
|
||||
|
||||
return '';
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException(sprintf('Exception was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
$encoded = base64_encode($data);
|
||||
|
||||
try {
|
||||
// We use a single MERGE SQL query when supported by the database.
|
||||
$mergeSql = $this->getMergeSql();
|
||||
|
||||
if (null !== $mergeSql) {
|
||||
$mergeStmt = $this->con->prepare($mergeSql);
|
||||
$mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$mergeStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
|
||||
$mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
|
||||
// Oracle has a bug that will intermittently happen if you
|
||||
// have only 1 bind on a CLOB field for 2 different statements
|
||||
// (INSERT and UPDATE in this case)
|
||||
if ('oracle' == $this->con->getDatabasePlatform()->getName()) {
|
||||
$mergeStmt->bindParam(':data2', $encoded, \PDO::PARAM_STR);
|
||||
}
|
||||
|
||||
$mergeStmt->execute();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$updateStmt = $this->con->prepare(
|
||||
"UPDATE $this->table SET $this->dataCol = :data, $this->timeCol = :time WHERE $this->idCol = :id"
|
||||
);
|
||||
$updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$updateStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
|
||||
$updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
$updateStmt->execute();
|
||||
|
||||
// When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
|
||||
// duplicate key errors when the same session is written simultaneously. We can just catch such an
|
||||
// error and re-execute the update. This is similar to a serializable transaction with retry logic
|
||||
// on serialization failures but without the overhead and without possible false positives due to
|
||||
// longer gap locking.
|
||||
if (!$updateStmt->rowCount()) {
|
||||
try {
|
||||
$insertStmt = $this->con->prepare(
|
||||
"INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)"
|
||||
);
|
||||
$insertStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||
$insertStmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
|
||||
$insertStmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
||||
$insertStmt->execute();
|
||||
} catch (\Exception $e) {
|
||||
$driverException = $e->getPrevious();
|
||||
// Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
|
||||
// DriverException only available since DBAL 2.5
|
||||
if (
|
||||
($driverException instanceof DriverException && 0 === strpos($driverException->getSQLState(), '23')) ||
|
||||
($driverException instanceof \PDOException && 0 === strpos($driverException->getCode(), '23'))
|
||||
) {
|
||||
$updateStmt->execute();
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a merge/upsert (i.e. insert or update) SQL query when supported by the database.
|
||||
*
|
||||
* @return string|null The SQL string or null when not supported
|
||||
*/
|
||||
private function getMergeSql()
|
||||
{
|
||||
$platform = $this->con->getDatabasePlatform()->getName();
|
||||
|
||||
switch (true) {
|
||||
case 'mysql' === $platform:
|
||||
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 'oracle' === $platform:
|
||||
// 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 = :data2, $this->timeCol = :time";
|
||||
case $this->con->getDatabasePlatform() instanceof SQLServer2008Platform:
|
||||
// MERGE is only available since SQL Server 2008 and must be terminated by semicolon
|
||||
// It also requires HOLDLOCK according to http://weblogs.sqlteam.com/dang/archive/2009/01/31/UPSERT-Race-Condition-With-MERGE.aspx
|
||||
return "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 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, $this->timeCol = :time;";
|
||||
case 'sqlite' === $platform:
|
||||
return "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time)";
|
||||
case 'postgresql' === $platform && version_compare($this->getServerVersion(), '9.5', '>='):
|
||||
return "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->timeCol) VALUES (:id, :data, :time) ".
|
||||
"ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->timeCol)";
|
||||
}
|
||||
}
|
||||
|
||||
private function getServerVersion()
|
||||
{
|
||||
$params = $this->con->getParams();
|
||||
|
||||
// Explicit platform version requested (supersedes auto-detection), so we respect it.
|
||||
if (isset($params['serverVersion'])) {
|
||||
return $params['serverVersion'];
|
||||
}
|
||||
|
||||
$wrappedConnection = $this->con->getWrappedConnection();
|
||||
|
||||
if ($wrappedConnection instanceof ServerInfoAwareConnection) {
|
||||
return $wrappedConnection->getServerVersion();
|
||||
}
|
||||
|
||||
// Support DBAL 2.4 by accessing it directly when using PDO PgSQL
|
||||
if ($wrappedConnection instanceof \PDO) {
|
||||
return $wrappedConnection->getAttribute(\PDO::ATTR_SERVER_VERSION);
|
||||
}
|
||||
|
||||
// If we cannot guess the version, the empty string will mean we won't use the code for newer versions when doing version checks.
|
||||
return '';
|
||||
}
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\HttpFoundation;
|
||||
|
||||
@trigger_error(sprintf('The class %s is deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::createTable instead.', DbalSessionHandlerSchema::class), E_USER_DEPRECATED);
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
|
||||
/**
|
||||
* DBAL Session Storage Schema.
|
||||
*
|
||||
* @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
||||
*
|
||||
* @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\PdoSessionHandler::createTable instead.
|
||||
*/
|
||||
final class DbalSessionHandlerSchema extends Schema
|
||||
{
|
||||
public function __construct($tableName = 'sessions')
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->addSessionTable($tableName);
|
||||
}
|
||||
|
||||
public function addToSchema(Schema $schema)
|
||||
{
|
||||
foreach ($this->getTables() as $table) {
|
||||
$schema->_addTable($table);
|
||||
}
|
||||
}
|
||||
|
||||
private function addSessionTable($tableName)
|
||||
{
|
||||
$table = $this->createTable($tableName);
|
||||
$table->addColumn('sess_id', 'string');
|
||||
$table->addColumn('sess_data', 'text')->setNotNull(true);
|
||||
$table->addColumn('sess_time', 'integer')->setNotNull(true)->setUnsigned(true);
|
||||
$table->setPrimaryKey(array('sess_id'));
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Bridge\Doctrine\Tests\HttpFoundation;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler;
|
||||
|
||||
/**
|
||||
* Test class for DbalSessionHandler.
|
||||
*
|
||||
* @author Drak <drak@zikula.org>
|
||||
*
|
||||
* @group legacy
|
||||
*/
|
||||
class DbalSessionHandlerTest extends TestCase
|
||||
{
|
||||
public function testConstruct()
|
||||
{
|
||||
$connection = $this->getMockBuilder('Doctrine\DBAL\Connection')->disableOriginalConstructor()->getMock();
|
||||
$handler = new DbalSessionHandler($connection);
|
||||
|
||||
$this->assertInstanceOf('Symfony\Bridge\Doctrine\HttpFoundation\DbalSessionHandler', $handler);
|
||||
}
|
||||
}
|
@ -1,121 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
|
||||
@trigger_error(sprintf('The class %s is deprecated since version 3.4 and will be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.', MemcacheSessionHandler::class), E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* @author Drak <drak@zikula.org>
|
||||
*
|
||||
* @deprecated since version 3.4, to be removed in 4.0. Use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcachedSessionHandler instead.
|
||||
*/
|
||||
class MemcacheSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var \Memcache Memcache driver
|
||||
*/
|
||||
private $memcache;
|
||||
|
||||
/**
|
||||
* @var int Time to live in seconds
|
||||
*/
|
||||
private $ttl;
|
||||
|
||||
/**
|
||||
* @var string Key prefix for shared environments
|
||||
*/
|
||||
private $prefix;
|
||||
|
||||
/**
|
||||
* List of available options:
|
||||
* * prefix: The prefix to use for the memcache keys in order to avoid collision
|
||||
* * expiretime: The time to live in seconds
|
||||
*
|
||||
* @param \Memcache $memcache A \Memcache instance
|
||||
* @param array $options An associative array of Memcache options
|
||||
*
|
||||
* @throws \InvalidArgumentException When unsupported options are passed
|
||||
*/
|
||||
public function __construct(\Memcache $memcache, array $options = array())
|
||||
{
|
||||
if ($diff = array_diff(array_keys($options), array('prefix', 'expiretime'))) {
|
||||
throw new \InvalidArgumentException(sprintf(
|
||||
'The following options are not supported "%s"', implode(', ', $diff)
|
||||
));
|
||||
}
|
||||
|
||||
$this->memcache = $memcache;
|
||||
$this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
|
||||
$this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($sessionId)
|
||||
{
|
||||
return $this->memcache->get($this->prefix.$sessionId) ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
return $this->memcache->set($this->prefix.$sessionId, $data, 0, time() + $this->ttl);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
$this->memcache->delete($this->prefix.$sessionId);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
// not required here because memcache will auto expire the records anyhow.
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Memcache instance.
|
||||
*
|
||||
* @return \Memcache
|
||||
*/
|
||||
protected function getMemcache()
|
||||
{
|
||||
return $this->memcache;
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var \Mongo|\MongoClient|\MongoDB\Client
|
||||
* @var \MongoDB\Client
|
||||
*/
|
||||
private $mongo;
|
||||
|
||||
@ -65,19 +65,10 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
* @param \MongoDB\Client $mongo A MongoDB\Client instance
|
||||
* @param array $options An associative array of field options
|
||||
*
|
||||
* @throws \InvalidArgumentException When MongoClient or Mongo instance not provided
|
||||
* @throws \InvalidArgumentException When "database" or "collection" not provided
|
||||
*/
|
||||
public function __construct($mongo, array $options)
|
||||
public function __construct(\MongoDB\Client $mongo, array $options)
|
||||
{
|
||||
if ($mongo instanceof \MongoClient || $mongo instanceof \Mongo) {
|
||||
@trigger_error(sprintf('Using %s with the legacy mongo extension is deprecated as of 3.4 and will be removed in 4.0. Use it with the mongodb/mongodb package and ext-mongodb instead.', __CLASS__), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if (!($mongo instanceof \MongoDB\Client || $mongo instanceof \MongoClient || $mongo instanceof \Mongo)) {
|
||||
throw new \InvalidArgumentException('MongoClient or Mongo instance required');
|
||||
}
|
||||
|
||||
if (!isset($options['database']) || !isset($options['collection'])) {
|
||||
throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler');
|
||||
}
|
||||
@ -113,9 +104,7 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
public function destroy($sessionId)
|
||||
{
|
||||
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
|
||||
|
||||
$this->getCollection()->$methodName(array(
|
||||
$this->getCollection()->deleteOne(array(
|
||||
$this->options['id_field'] => $sessionId,
|
||||
));
|
||||
|
||||
@ -127,10 +116,8 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
public function gc($maxlifetime)
|
||||
{
|
||||
$methodName = $this->mongo instanceof \MongoDB\Client ? 'deleteOne' : 'remove';
|
||||
|
||||
$this->getCollection()->$methodName(array(
|
||||
$this->options['expiry_field'] => array('$lt' => $this->createDateTime()),
|
||||
$this->getCollection()->deleteOne(array(
|
||||
$this->options['expiry_field'] => array('$lt' => new \MongoDB\BSON\UTCDateTime()),
|
||||
));
|
||||
|
||||
return true;
|
||||
@ -141,28 +128,18 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
public function write($sessionId, $data)
|
||||
{
|
||||
$expiry = $this->createDateTime(time() + (int) ini_get('session.gc_maxlifetime'));
|
||||
$expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
|
||||
|
||||
$fields = array(
|
||||
$this->options['time_field'] => $this->createDateTime(),
|
||||
$this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
|
||||
$this->options['expiry_field'] => $expiry,
|
||||
$this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY),
|
||||
);
|
||||
|
||||
$options = array('upsert' => true);
|
||||
|
||||
if ($this->mongo instanceof \MongoDB\Client) {
|
||||
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
|
||||
} else {
|
||||
$fields[$this->options['data_field']] = new \MongoBinData($data, \MongoBinData::BYTE_ARRAY);
|
||||
$options['multiple'] = false;
|
||||
}
|
||||
|
||||
$methodName = $this->mongo instanceof \MongoDB\Client ? 'updateOne' : 'update';
|
||||
|
||||
$this->getCollection()->$methodName(
|
||||
$this->getCollection()->updateOne(
|
||||
array($this->options['id_field'] => $sessionId),
|
||||
array('$set' => $fields),
|
||||
$options
|
||||
array('upsert' => true)
|
||||
);
|
||||
|
||||
return true;
|
||||
@ -175,23 +152,17 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
$dbData = $this->getCollection()->findOne(array(
|
||||
$this->options['id_field'] => $sessionId,
|
||||
$this->options['expiry_field'] => array('$gte' => $this->createDateTime()),
|
||||
$this->options['expiry_field'] => array('$gte' => new \MongoDB\BSON\UTCDateTime()),
|
||||
));
|
||||
|
||||
if (null === $dbData) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if ($dbData[$this->options['data_field']] instanceof \MongoDB\BSON\Binary) {
|
||||
return $dbData[$this->options['data_field']]->getData();
|
||||
}
|
||||
|
||||
return $dbData[$this->options['data_field']]->bin;
|
||||
return $dbData[$this->options['data_field']]->getData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a "MongoCollection" instance.
|
||||
*
|
||||
* @return \MongoCollection
|
||||
*/
|
||||
private function getCollection()
|
||||
@ -204,34 +175,10 @@ class MongoDbSessionHandler implements \SessionHandlerInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a Mongo instance.
|
||||
*
|
||||
* @return \Mongo|\MongoClient|\MongoDB\Client
|
||||
* @return \MongoDB\Client
|
||||
*/
|
||||
protected function getMongo()
|
||||
{
|
||||
return $this->mongo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a date object using the class appropriate for the current mongo connection.
|
||||
*
|
||||
* Return an instance of a MongoDate or \MongoDB\BSON\UTCDateTime
|
||||
*
|
||||
* @param int $seconds An integer representing UTC seconds since Jan 1 1970. Defaults to now.
|
||||
*
|
||||
* @return \MongoDate|\MongoDB\BSON\UTCDateTime
|
||||
*/
|
||||
private function createDateTime($seconds = null)
|
||||
{
|
||||
if (null === $seconds) {
|
||||
$seconds = time();
|
||||
}
|
||||
|
||||
if ($this->mongo instanceof \MongoDB\Client) {
|
||||
return new \MongoDB\BSON\UTCDateTime($seconds * 1000);
|
||||
}
|
||||
|
||||
return new \MongoDate($seconds);
|
||||
}
|
||||
}
|
||||
|
@ -1,130 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\HttpFoundation\Tests\Session\Storage\Handler;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\HttpFoundation\Session\Storage\Handler\MemcacheSessionHandler;
|
||||
|
||||
/**
|
||||
* @requires extension memcache
|
||||
* @group time-sensitive
|
||||
* @group legacy
|
||||
*/
|
||||
class MemcacheSessionHandlerTest extends TestCase
|
||||
{
|
||||
const PREFIX = 'prefix_';
|
||||
const TTL = 1000;
|
||||
/**
|
||||
* @var MemcacheSessionHandler
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
protected $memcache;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
$this->memcache = $this->getMockBuilder('Memcache')->getMock();
|
||||
$this->storage = new MemcacheSessionHandler(
|
||||
$this->memcache,
|
||||
array('prefix' => self::PREFIX, 'expiretime' => self::TTL)
|
||||
);
|
||||
}
|
||||
|
||||
protected function tearDown()
|
||||
{
|
||||
$this->memcache = null;
|
||||
$this->storage = null;
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testOpenSession()
|
||||
{
|
||||
$this->assertTrue($this->storage->open('', ''));
|
||||
}
|
||||
|
||||
public function testCloseSession()
|
||||
{
|
||||
$this->assertTrue($this->storage->close());
|
||||
}
|
||||
|
||||
public function testReadSession()
|
||||
{
|
||||
$this->memcache
|
||||
->expects($this->once())
|
||||
->method('get')
|
||||
->with(self::PREFIX.'id')
|
||||
;
|
||||
|
||||
$this->assertEquals('', $this->storage->read('id'));
|
||||
}
|
||||
|
||||
public function testWriteSession()
|
||||
{
|
||||
$this->memcache
|
||||
->expects($this->once())
|
||||
->method('set')
|
||||
->with(self::PREFIX.'id', 'data', 0, $this->equalTo(time() + self::TTL, 2))
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($this->storage->write('id', 'data'));
|
||||
}
|
||||
|
||||
public function testDestroySession()
|
||||
{
|
||||
$this->memcache
|
||||
->expects($this->once())
|
||||
->method('delete')
|
||||
->with(self::PREFIX.'id')
|
||||
->will($this->returnValue(true))
|
||||
;
|
||||
|
||||
$this->assertTrue($this->storage->destroy('id'));
|
||||
}
|
||||
|
||||
public function testGcSession()
|
||||
{
|
||||
$this->assertTrue($this->storage->gc(123));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider getOptionFixtures
|
||||
*/
|
||||
public function testSupportedOptions($options, $supported)
|
||||
{
|
||||
try {
|
||||
new MemcacheSessionHandler($this->memcache, $options);
|
||||
$this->assertTrue($supported);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
$this->assertFalse($supported);
|
||||
}
|
||||
}
|
||||
|
||||
public function getOptionFixtures()
|
||||
{
|
||||
return array(
|
||||
array(array('prefix' => 'session'), true),
|
||||
array(array('expiretime' => 100), true),
|
||||
array(array('prefix' => 'session', 'expiretime' => 200), true),
|
||||
array(array('expiretime' => 100, 'foo' => 'bar'), false),
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetConnection()
|
||||
{
|
||||
$method = new \ReflectionMethod($this->storage, 'getMemcache');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertInstanceOf('\Memcache', $method->invoke($this->storage));
|
||||
}
|
||||
}
|
@ -32,11 +32,11 @@ class MongoDbSessionHandlerTest extends TestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
if (!class_exists('MongoDB\Client')) {
|
||||
if (!class_exists(\MongoDB\Client::class)) {
|
||||
$this->markTestSkipped('The mongodb/mongodb package is required.');
|
||||
}
|
||||
|
||||
$this->mongo = $this->getMockBuilder('MongoDB\Client')
|
||||
$this->mongo = $this->getMockBuilder(\MongoDB\Client::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
@ -52,14 +52,6 @@ class MongoDbSessionHandlerTest extends TestCase
|
||||
$this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
public function testConstructorShouldThrowExceptionForInvalidMongo()
|
||||
{
|
||||
new MongoDbSessionHandler(new \stdClass(), $this->options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \InvalidArgumentException
|
||||
*/
|
||||
@ -100,27 +92,14 @@ class MongoDbSessionHandlerTest extends TestCase
|
||||
$this->assertArrayHasKey($this->options['expiry_field'], $criteria);
|
||||
$this->assertArrayHasKey('$gte', $criteria[$this->options['expiry_field']]);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$gte']);
|
||||
$this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout);
|
||||
} else {
|
||||
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$gte']);
|
||||
$this->assertGreaterThanOrEqual($criteria[$this->options['expiry_field']]['$gte']->sec, $testTimeout);
|
||||
}
|
||||
$this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $criteria[$this->options['expiry_field']]['$gte']);
|
||||
$this->assertGreaterThanOrEqual(round((string) $criteria[$this->options['expiry_field']]['$gte'] / 1000), $testTimeout);
|
||||
|
||||
$fields = array(
|
||||
return array(
|
||||
$this->options['id_field'] => 'foo',
|
||||
$this->options['expiry_field'] => new \MongoDB\BSON\UTCDateTime(),
|
||||
$this->options['data_field'] => new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY),
|
||||
);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$fields[$this->options['data_field']] = new \MongoDB\BSON\Binary('bar', \MongoDB\BSON\Binary::TYPE_OLD_BINARY);
|
||||
$fields[$this->options['id_field']] = new \MongoDB\BSON\UTCDateTime(time() * 1000);
|
||||
} else {
|
||||
$fields[$this->options['data_field']] = new \MongoBinData('bar', \MongoBinData::BYTE_ARRAY);
|
||||
$fields[$this->options['id_field']] = new \MongoDate();
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}));
|
||||
|
||||
$this->assertEquals('bar', $this->storage->read('foo'));
|
||||
@ -135,89 +114,22 @@ class MongoDbSessionHandlerTest extends TestCase
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$data = array();
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method($methodName)
|
||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
|
||||
->method('updateOne')
|
||||
->will($this->returnCallback(function ($criteria, $updateData, $options) {
|
||||
$this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals(array('upsert' => true), $options);
|
||||
} else {
|
||||
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
|
||||
}
|
||||
|
||||
$data = $updateData['$set'];
|
||||
}));
|
||||
|
||||
$expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
|
||||
$this->assertTrue($this->storage->write('foo', 'bar'));
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->getData());
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
|
||||
$this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000));
|
||||
} else {
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
|
||||
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
|
||||
$this->assertGreaterThanOrEqual($expectedExpiry, $data[$this->options['expiry_field']]->sec);
|
||||
}
|
||||
}
|
||||
|
||||
public function testWriteWhenUsingExpiresField()
|
||||
{
|
||||
$this->options = array(
|
||||
'id_field' => '_id',
|
||||
'data_field' => 'data',
|
||||
'time_field' => 'time',
|
||||
'database' => 'sf2-test',
|
||||
'collection' => 'session-test',
|
||||
'expiry_field' => 'expiresAt',
|
||||
);
|
||||
|
||||
$this->storage = new MongoDbSessionHandler($this->mongo, $this->options);
|
||||
|
||||
$collection = $this->createMongoCollectionMock();
|
||||
|
||||
$this->mongo->expects($this->once())
|
||||
->method('selectCollection')
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$data = array();
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method($methodName)
|
||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
|
||||
$this->assertEquals(array($this->options['id_field'] => 'foo'), $criteria);
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals(array('upsert' => true), $options);
|
||||
} else {
|
||||
$this->assertEquals(array('upsert' => true, 'multiple' => false), $options);
|
||||
}
|
||||
$this->assertEquals(array('upsert' => true), $options);
|
||||
|
||||
$data = $updateData['$set'];
|
||||
$expectedExpiry = time() + (int) ini_get('session.gc_maxlifetime');
|
||||
$this->assertInstanceOf(\MongoDB\BSON\Binary::class, $data[$this->options['data_field']]);
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->getData());
|
||||
$this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $data[$this->options['expiry_field']]);
|
||||
$this->assertGreaterThanOrEqual($expectedExpiry, round((string) $data[$this->options['expiry_field']] / 1000));
|
||||
}));
|
||||
|
||||
$this->assertTrue($this->storage->write('foo', 'bar'));
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->getData());
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $data[$this->options['expiry_field']]);
|
||||
} else {
|
||||
$this->assertEquals('bar', $data[$this->options['data_field']]->bin);
|
||||
$this->assertInstanceOf('MongoDate', $data[$this->options['time_field']]);
|
||||
$this->assertInstanceOf('MongoDate', $data[$this->options['expiry_field']]);
|
||||
}
|
||||
}
|
||||
|
||||
public function testReplaceSessionData()
|
||||
@ -231,10 +143,8 @@ class MongoDbSessionHandlerTest extends TestCase
|
||||
|
||||
$data = array();
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'updateOne' : 'update';
|
||||
|
||||
$collection->expects($this->exactly(2))
|
||||
->method($methodName)
|
||||
->method('updateOne')
|
||||
->will($this->returnCallback(function ($criteria, $updateData, $options) use (&$data) {
|
||||
$data = $updateData;
|
||||
}));
|
||||
@ -242,11 +152,7 @@ class MongoDbSessionHandlerTest extends TestCase
|
||||
$this->storage->write('foo', 'bar');
|
||||
$this->storage->write('foo', 'foobar');
|
||||
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
|
||||
} else {
|
||||
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->bin);
|
||||
}
|
||||
$this->assertEquals('foobar', $data['$set'][$this->options['data_field']]->getData());
|
||||
}
|
||||
|
||||
public function testDestroy()
|
||||
@ -258,10 +164,8 @@ class MongoDbSessionHandlerTest extends TestCase
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method($methodName)
|
||||
->method('deleteOne')
|
||||
->with(array($this->options['id_field'] => 'foo'));
|
||||
|
||||
$this->assertTrue($this->storage->destroy('foo'));
|
||||
@ -276,18 +180,11 @@ class MongoDbSessionHandlerTest extends TestCase
|
||||
->with($this->options['database'], $this->options['collection'])
|
||||
->will($this->returnValue($collection));
|
||||
|
||||
$methodName = phpversion('mongodb') ? 'deleteOne' : 'remove';
|
||||
|
||||
$collection->expects($this->once())
|
||||
->method($methodName)
|
||||
->method('deleteOne')
|
||||
->will($this->returnCallback(function ($criteria) {
|
||||
if (phpversion('mongodb')) {
|
||||
$this->assertInstanceOf('MongoDB\BSON\UTCDateTime', $criteria[$this->options['expiry_field']]['$lt']);
|
||||
$this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000));
|
||||
} else {
|
||||
$this->assertInstanceOf('MongoDate', $criteria[$this->options['expiry_field']]['$lt']);
|
||||
$this->assertGreaterThanOrEqual(time() - 1, $criteria[$this->options['expiry_field']]['$lt']->sec);
|
||||
}
|
||||
$this->assertInstanceOf(\MongoDB\BSON\UTCDateTime::class, $criteria[$this->options['expiry_field']]['$lt']);
|
||||
$this->assertGreaterThanOrEqual(time() - 1, round((string) $criteria[$this->options['expiry_field']]['$lt'] / 1000));
|
||||
}));
|
||||
|
||||
$this->assertTrue($this->storage->gc(1));
|
||||
@ -298,12 +195,12 @@ class MongoDbSessionHandlerTest extends TestCase
|
||||
$method = new \ReflectionMethod($this->storage, 'getMongo');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$this->assertInstanceOf('MongoDB\Client', $method->invoke($this->storage));
|
||||
$this->assertInstanceOf(\MongoDB\Client::class, $method->invoke($this->storage));
|
||||
}
|
||||
|
||||
private function createMongoCollectionMock()
|
||||
{
|
||||
$collection = $this->getMockBuilder('MongoDB\Collection')
|
||||
$collection = $this->getMockBuilder(\MongoDB\Collection::class)
|
||||
->disableOriginalConstructor()
|
||||
->getMock();
|
||||
|
||||
|
@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\VarDumper\Caster;
|
||||
|
||||
use Symfony\Component\VarDumper\Cloner\Stub;
|
||||
|
||||
@trigger_error('The '.__NAMESPACE__.'\MongoCaster class is deprecated since version 3.4 and will be removed in 4.0.', E_USER_DEPRECATED);
|
||||
|
||||
/**
|
||||
* Casts classes from the MongoDb extension to array representation.
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*
|
||||
* @deprecated since version 3.4, to be removed in 4.0.
|
||||
*/
|
||||
class MongoCaster
|
||||
{
|
||||
public static function castCursor(\MongoCursorInterface $cursor, array $a, Stub $stub, $isNested)
|
||||
{
|
||||
if ($info = $cursor->info()) {
|
||||
foreach ($info as $k => $v) {
|
||||
$a[Caster::PREFIX_VIRTUAL.$k] = $v;
|
||||
}
|
||||
}
|
||||
$a[Caster::PREFIX_VIRTUAL.'dead'] = $cursor->dead();
|
||||
|
||||
return $a;
|
||||
}
|
||||
}
|
@ -104,8 +104,6 @@ abstract class AbstractCloner implements ClonerInterface
|
||||
'SplPriorityQueue' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'),
|
||||
'OuterIterator' => array('Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'),
|
||||
|
||||
'MongoCursorInterface' => array('Symfony\Component\VarDumper\Caster\MongoCaster', 'castCursor'),
|
||||
|
||||
'Redis' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'),
|
||||
'RedisArray' => array('Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'),
|
||||
|
||||
|
Reference in New Issue
Block a user