[HttpFoundation] implement lazy connect for pdo session handler
This commit is contained in:
parent
7dad54ca08
commit
251238d9a6
@ -37,10 +37,15 @@ namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
|
||||
class PdoSessionHandler implements \SessionHandlerInterface
|
||||
{
|
||||
/**
|
||||
* @var \PDO PDO instance
|
||||
* @var \PDO|null PDO instance or null when not connected yet
|
||||
*/
|
||||
private $pdo;
|
||||
|
||||
/**
|
||||
* @var string|null|false DNS string or null for session.save_path or false when lazy connection disabled
|
||||
*/
|
||||
private $dns = false;
|
||||
|
||||
/**
|
||||
* @var string Database driver
|
||||
*/
|
||||
@ -66,6 +71,21 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||
*/
|
||||
private $timeCol;
|
||||
|
||||
/**
|
||||
* @var string Username when lazy-connect
|
||||
*/
|
||||
private $username;
|
||||
|
||||
/**
|
||||
* @var string Password when lazy-connect
|
||||
*/
|
||||
private $password;
|
||||
|
||||
/**
|
||||
* @var array Connection options when lazy-connect
|
||||
*/
|
||||
private $connectionOptions = array();
|
||||
|
||||
/**
|
||||
* @var bool Whether a transaction is active
|
||||
*/
|
||||
@ -79,37 +99,54 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* You can either pass an existing database connection as PDO instance or
|
||||
* pass a DNS string that will be used to lazy-connect to the database
|
||||
* when the session is actually used. Furthermore it's possible to pass null
|
||||
* which will then use the session.save_path ini setting as PDO DNS parameter.
|
||||
*
|
||||
* List of available options:
|
||||
* * db_table: The name of the table [default: sessions]
|
||||
* * db_id_col: The column where to store the session id [default: sess_id]
|
||||
* * db_data_col: The column where to store the session data [default: sess_data]
|
||||
* * db_time_col: The column where to store the timestamp [default: sess_time]
|
||||
* * db_username: The username when lazy-connect [default: '']
|
||||
* * db_password: The password when lazy-connect [default: '']
|
||||
* * db_connection_options: An array of driver-specific connection options [default: array()]
|
||||
*
|
||||
* @param \PDO $pdo A \PDO instance
|
||||
* @param \PDO|string|null $pdoOrDns A \PDO instance or DNS string or null
|
||||
* @param array $options An associative array of DB options
|
||||
*
|
||||
* @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
|
||||
*/
|
||||
public function __construct(\PDO $pdo, array $options = array())
|
||||
public function __construct($pdoOrDns, array $options = array())
|
||||
{
|
||||
if (\PDO::ERRMODE_EXCEPTION !== $pdo->getAttribute(\PDO::ATTR_ERRMODE)) {
|
||||
if ($pdoOrDns instanceof \PDO) {
|
||||
if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDns->getAttribute(\PDO::ATTR_ERRMODE)) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
|
||||
}
|
||||
|
||||
$this->pdo = $pdo;
|
||||
$this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||
$this->pdo = $pdoOrDns;
|
||||
} else {
|
||||
$this->dns = $pdoOrDns;
|
||||
}
|
||||
|
||||
$options = array_replace(array(
|
||||
'db_table' => 'sessions',
|
||||
'db_id_col' => 'sess_id',
|
||||
'db_data_col' => 'sess_data',
|
||||
'db_time_col' => 'sess_time',
|
||||
'db_username' => '',
|
||||
'db_password' => '',
|
||||
'db_connection_options' => array()
|
||||
), $options);
|
||||
|
||||
$this->table = $options['db_table'];
|
||||
$this->idCol = $options['db_id_col'];
|
||||
$this->dataCol = $options['db_data_col'];
|
||||
$this->timeCol = $options['db_time_col'];
|
||||
$this->username = $options['db_username'];
|
||||
$this->password = $options['db_password'];
|
||||
$this->connectionOptions = $options['db_connection_options'];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,6 +155,11 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||
public function open($savePath, $sessionName)
|
||||
{
|
||||
$this->gcCalled = false;
|
||||
if (null === $this->pdo) {
|
||||
$this->pdo = new \PDO($this->dns ?: $savePath, $this->username, $this->password, $this->connectionOptions);
|
||||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
}
|
||||
$this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -270,6 +312,10 @@ class PdoSessionHandler implements \SessionHandlerInterface
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
if (false !== $this->dns) {
|
||||
$this->pdo = null;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -25,7 +25,7 @@ class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$this->pdo = new \PDO('sqlite::memory:');
|
||||
$this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
|
||||
$sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data TEXT, sess_time INTEGER)';
|
||||
$sql = 'CREATE TABLE sessions (sess_id VARCHAR(128) PRIMARY KEY, sess_data BLOB, sess_time INTEGER)';
|
||||
$this->pdo->exec($sql);
|
||||
}
|
||||
|
||||
@ -51,17 +51,74 @@ class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$storage->close();
|
||||
}
|
||||
|
||||
public function testWithLazyDnsConnection()
|
||||
{
|
||||
$dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions');
|
||||
if (file_exists($dbFile)) {
|
||||
@unlink($dbFile);
|
||||
}
|
||||
|
||||
$pdo = new \PDO('sqlite:' . $dbFile);
|
||||
$sql = 'CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data BLOB, sess_time INTEGER)';
|
||||
$pdo->exec($sql);
|
||||
$pdo = null;
|
||||
|
||||
$storage = new PdoSessionHandler('sqlite:' . $dbFile);
|
||||
$storage->open('', 'sid');
|
||||
$data = $storage->read('id');
|
||||
$storage->write('id', 'data');
|
||||
$storage->close();
|
||||
$this->assertSame('', $data, 'New session returns empty string data');
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$data = $storage->read('id');
|
||||
$storage->close();
|
||||
$this->assertSame('data', $data, 'Written value can be read back correctly');
|
||||
|
||||
@unlink($dbFile);
|
||||
}
|
||||
|
||||
public function testWithLazySavePathConnection()
|
||||
{
|
||||
$dbFile = tempnam(sys_get_temp_dir(), 'sf2_sqlite_sessions');
|
||||
if (file_exists($dbFile)) {
|
||||
@unlink($dbFile);
|
||||
}
|
||||
|
||||
$pdo = new \PDO('sqlite:' . $dbFile);
|
||||
$sql = 'CREATE TABLE sessions (sess_id VARCHAR(255) PRIMARY KEY, sess_data BLOB, sess_time INTEGER)';
|
||||
$pdo->exec($sql);
|
||||
$pdo = null;
|
||||
|
||||
// Open is called with what ini_set('session.save_path', 'sqlite:' . $dbFile) would mean
|
||||
$storage = new PdoSessionHandler(null);
|
||||
$storage->open('sqlite:' . $dbFile, 'sid');
|
||||
$data = $storage->read('id');
|
||||
$storage->write('id', 'data');
|
||||
$storage->close();
|
||||
$this->assertSame('', $data, 'New session returns empty string data');
|
||||
|
||||
$storage->open('sqlite:' . $dbFile, 'sid');
|
||||
$data = $storage->read('id');
|
||||
$storage->close();
|
||||
$this->assertSame('data', $data, 'Written value can be read back correctly');
|
||||
|
||||
@unlink($dbFile);
|
||||
}
|
||||
|
||||
public function testReadWriteRead()
|
||||
{
|
||||
$storage = new PdoSessionHandler($this->pdo);
|
||||
$storage->open('', 'sid');
|
||||
$this->assertSame('', $storage->read('id'), 'New session returns empty string data');
|
||||
$data = $storage->read('id');
|
||||
$storage->write('id', 'data');
|
||||
$storage->close();
|
||||
$this->assertSame('', $data, 'New session returns empty string data');
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$this->assertSame('data', $storage->read('id'), 'Written value can be read back correctly');
|
||||
$data = $storage->read('id');
|
||||
$storage->close();
|
||||
$this->assertSame('data', $data, 'Written value can be read back correctly');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,8 +134,9 @@ class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$storage->close();
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$this->assertSame('data_of_new_session_id', $storage->read('new_id'), 'Data of regenerated session id is available');
|
||||
$data = $storage->read('new_id');
|
||||
$storage->close();
|
||||
$this->assertSame('data_of_new_session_id', $data, 'Data of regenerated session id is available');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,8 +167,9 @@ class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$this->assertSame('', $storage->read('id'), 'Destroyed session returns empty string');
|
||||
$data = $storage->read('id');
|
||||
$storage->close();
|
||||
$this->assertSame('', $data, 'Destroyed session returns empty string');
|
||||
}
|
||||
|
||||
public function testSessionGC()
|
||||
@ -125,12 +184,14 @@ class PdoSessionHandlerTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(1, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
|
||||
|
||||
$storage->open('', 'sid');
|
||||
$this->assertSame('', $storage->read('id'), 'Session already considered garbage, so not returning data even if it is not pruned yet');
|
||||
$data = $storage->read('id');
|
||||
$storage->gc(0);
|
||||
$storage->close();
|
||||
$this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
|
||||
|
||||
ini_set('session.gc_maxlifetime', $previousLifeTime);
|
||||
|
||||
$this->assertSame('', $data, 'Session already considered garbage, so not returning data even if it is not pruned yet');
|
||||
$this->assertEquals(0, $this->pdo->query('SELECT COUNT(*) FROM sessions')->fetchColumn());
|
||||
}
|
||||
|
||||
public function testGetConnection()
|
||||
|
Reference in New Issue
Block a user