mirror of
https://github.com/Yetangitu/ampache
synced 2025-10-05 10:49:37 +02:00
253 lines
8.5 KiB
PHP
253 lines
8.5 KiB
PHP
<?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;
|
|
|
|
/**
|
|
* PdoSessionHandler.
|
|
*
|
|
* @author Fabien Potencier <fabien@symfony.com>
|
|
* @author Michael Williams <michael.williams@funsational.com>
|
|
*/
|
|
class PdoSessionHandler implements \SessionHandlerInterface
|
|
{
|
|
/**
|
|
* @var \PDO PDO instance.
|
|
*/
|
|
private $pdo;
|
|
|
|
/**
|
|
* @var array Database options.
|
|
*/
|
|
private $dbOptions;
|
|
|
|
/**
|
|
* Constructor.
|
|
*
|
|
* List of available options:
|
|
* * db_table: The name of the table [required]
|
|
* * 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]
|
|
*
|
|
* @param \PDO $pdo A \PDO instance
|
|
* @param array $dbOptions An associative array of DB options
|
|
*
|
|
* @throws \InvalidArgumentException When "db_table" option is not provided
|
|
*/
|
|
public function __construct(\PDO $pdo, array $dbOptions = array())
|
|
{
|
|
if (!array_key_exists('db_table', $dbOptions)) {
|
|
throw new \InvalidArgumentException('You must provide the "db_table" option for a PdoSessionStorage.');
|
|
}
|
|
if (\PDO::ERRMODE_EXCEPTION !== $pdo->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->dbOptions = array_merge(array(
|
|
'db_id_col' => 'sess_id',
|
|
'db_data_col' => 'sess_data',
|
|
'db_time_col' => 'sess_time',
|
|
), $dbOptions);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function open($path, $name)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function close()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function destroy($id)
|
|
{
|
|
// get table/column
|
|
$dbTable = $this->dbOptions['db_table'];
|
|
$dbIdCol = $this->dbOptions['db_id_col'];
|
|
|
|
// delete the record associated with this id
|
|
$sql = "DELETE FROM $dbTable WHERE $dbIdCol = :id";
|
|
|
|
try {
|
|
$stmt = $this->pdo->prepare($sql);
|
|
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
|
|
$stmt->execute();
|
|
} catch (\PDOException $e) {
|
|
throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function gc($lifetime)
|
|
{
|
|
// get table/column
|
|
$dbTable = $this->dbOptions['db_table'];
|
|
$dbTimeCol = $this->dbOptions['db_time_col'];
|
|
|
|
// delete the session records that have expired
|
|
$sql = "DELETE FROM $dbTable WHERE $dbTimeCol < :time";
|
|
|
|
try {
|
|
$stmt = $this->pdo->prepare($sql);
|
|
$stmt->bindValue(':time', time() - $lifetime, \PDO::PARAM_INT);
|
|
$stmt->execute();
|
|
} catch (\PDOException $e) {
|
|
throw new \RuntimeException(sprintf('PDOException was thrown when trying to manipulate session data: %s', $e->getMessage()), 0, $e);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function read($id)
|
|
{
|
|
// get table/columns
|
|
$dbTable = $this->dbOptions['db_table'];
|
|
$dbDataCol = $this->dbOptions['db_data_col'];
|
|
$dbIdCol = $this->dbOptions['db_id_col'];
|
|
|
|
try {
|
|
$sql = "SELECT $dbDataCol FROM $dbTable WHERE $dbIdCol = :id";
|
|
|
|
$stmt = $this->pdo->prepare($sql);
|
|
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
|
|
|
|
$stmt->execute();
|
|
// it is recommended to use fetchAll so that PDO can close the DB cursor
|
|
// we anyway expect either no rows, or one row with one column. fetchColumn, seems to be buggy #4777
|
|
$sessionRows = $stmt->fetchAll(\PDO::FETCH_NUM);
|
|
|
|
if (count($sessionRows) == 1) {
|
|
return base64_decode($sessionRows[0][0]);
|
|
}
|
|
|
|
// session does not exist, create it
|
|
$this->createNewSession($id);
|
|
|
|
return '';
|
|
} catch (\PDOException $e) {
|
|
throw new \RuntimeException(sprintf('PDOException was thrown when trying to read the session data: %s', $e->getMessage()), 0, $e);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function write($id, $data)
|
|
{
|
|
// get table/column
|
|
$dbTable = $this->dbOptions['db_table'];
|
|
$dbDataCol = $this->dbOptions['db_data_col'];
|
|
$dbIdCol = $this->dbOptions['db_id_col'];
|
|
$dbTimeCol = $this->dbOptions['db_time_col'];
|
|
|
|
//session data can contain non binary safe characters so we need to encode it
|
|
$encoded = base64_encode($data);
|
|
|
|
try {
|
|
$driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
|
|
|
|
if ('mysql' === $driver) {
|
|
// MySQL would report $stmt->rowCount() = 0 on UPDATE when the data is left unchanged
|
|
// it could result in calling createNewSession() whereas the session already exists in
|
|
// the DB which would fail as the id is unique
|
|
$stmt = $this->pdo->prepare(
|
|
"INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time) " .
|
|
"ON DUPLICATE KEY UPDATE $dbDataCol = VALUES($dbDataCol), $dbTimeCol = VALUES($dbTimeCol)"
|
|
);
|
|
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
|
|
$stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
|
|
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
|
$stmt->execute();
|
|
} elseif ('oci' === $driver) {
|
|
$stmt = $this->pdo->prepare("MERGE INTO $dbTable USING DUAL ON($dbIdCol = :id) ".
|
|
"WHEN NOT MATCHED THEN INSERT ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, sysdate) " .
|
|
"WHEN MATCHED THEN UPDATE SET $dbDataCol = :data WHERE $dbIdCol = :id");
|
|
|
|
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
|
|
$stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
|
|
$stmt->execute();
|
|
} else {
|
|
$stmt = $this->pdo->prepare("UPDATE $dbTable SET $dbDataCol = :data, $dbTimeCol = :time WHERE $dbIdCol = :id");
|
|
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
|
|
$stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
|
|
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
|
$stmt->execute();
|
|
|
|
if (!$stmt->rowCount()) {
|
|
// No session exists in the database to update. This happens when we have called
|
|
// session_regenerate_id()
|
|
$this->createNewSession($id, $data);
|
|
}
|
|
}
|
|
} catch (\PDOException $e) {
|
|
throw new \RuntimeException(sprintf('PDOException was thrown when trying to write the session data: %s', $e->getMessage()), 0, $e);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Creates a new session with the given $id and $data
|
|
*
|
|
* @param string $id
|
|
* @param string $data
|
|
*
|
|
* @return boolean True.
|
|
*/
|
|
private function createNewSession($id, $data = '')
|
|
{
|
|
// get table/column
|
|
$dbTable = $this->dbOptions['db_table'];
|
|
$dbDataCol = $this->dbOptions['db_data_col'];
|
|
$dbIdCol = $this->dbOptions['db_id_col'];
|
|
$dbTimeCol = $this->dbOptions['db_time_col'];
|
|
|
|
$sql = "INSERT INTO $dbTable ($dbIdCol, $dbDataCol, $dbTimeCol) VALUES (:id, :data, :time)";
|
|
|
|
//session data can contain non binary safe characters so we need to encode it
|
|
$encoded = base64_encode($data);
|
|
$stmt = $this->pdo->prepare($sql);
|
|
$stmt->bindParam(':id', $id, \PDO::PARAM_STR);
|
|
$stmt->bindParam(':data', $encoded, \PDO::PARAM_STR);
|
|
$stmt->bindValue(':time', time(), \PDO::PARAM_INT);
|
|
$stmt->execute();
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Return a PDO instance
|
|
*
|
|
* @return \PDO
|
|
*/
|
|
protected function getConnection()
|
|
{
|
|
return $this->pdo;
|
|
}
|
|
}
|