Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make the session handlers all compatible with SessionHandlerInterface #5012

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@
RecastingRemovalRector::class => [
__DIR__ . '/tests/system/Entity/EntityTest.php',
],

// session handlers have the gc() method with underscored parameter `$max_lifetime`
UnderscoreToCamelCaseVariableNameRector::class => [
__DIR__ . '/system/Session/Handlers',
],
]);

// auto import fully qualified class names
Expand Down
60 changes: 26 additions & 34 deletions system/Session/Handlers/ArrayHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

namespace CodeIgniter\Session\Handlers;

use Exception;
use ReturnTypeWillChange;

/**
* Session handler using static array for storage.
Expand All @@ -22,78 +22,70 @@ class ArrayHandler extends BaseHandler
protected static $cache = [];

/**
* Open
* Re-initialize existing session, or creates a new one.
*
* Ensures we have an initialized database connection.
*
* @param string $savePath Path to session files' directory
* @param string $name Session cookie name
*
* @throws Exception
* @param string $path The path where to store/retrieve the session
* @param string $name The session name
*/
public function open($savePath, $name): bool
public function open($path, $name): bool
{
return true;
}

/**
* Read
* Reads the session data from the session storage, and returns the results.
*
* Reads session data and acquires a lock
* @param string $id The session ID
*
* @param string $sessionID Session ID
*
* @return string Serialized session data
* @return false|string Returns an encoded string of the read data.
* If nothing was read, it must return false.
*/
public function read($sessionID): string
#[ReturnTypeWillChange]
public function read($id)
{
return '';
}

/**
* Write
*
* Writes (create / update) session data
* Writes the session data to the session storage.
*
* @param string $sessionID Session ID
* @param string $sessionData Serialized session data
* @param string $id The session ID
* @param string $data The encoded session data
*/
public function write($sessionID, $sessionData): bool
public function write($id, $data): bool
{
return true;
}

/**
* Close
*
* Releases locks and closes file descriptor.
* Closes the current session.
*/
public function close(): bool
{
return true;
}

/**
* Destroy
*
* Destroys the current session.
* Destroys a session
*
* @param string $sessionID
* @param string $id The session ID being destroyed
*/
public function destroy($sessionID): bool
public function destroy($id): bool
{
return true;
}

/**
* Garbage Collector
* Cleans up expired sessions.
*
* Deletes expired sessions
* @param int $max_lifetime Sessions that have not updated
* for the last max_lifetime seconds will be removed.
*
* @param int $maxlifetime Maximum lifetime of sessions
* @return false|int Returns the number of deleted sessions on success, or false on failure.
*/
public function gc($maxlifetime): bool
#[ReturnTypeWillChange]
public function gc($max_lifetime)
paulbalandan marked this conversation as resolved.
Show resolved Hide resolved
{
return true;
return 1;
}
}
6 changes: 1 addition & 5 deletions system/Session/Handlers/BaseHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,6 @@ abstract class BaseHandler implements SessionHandlerInterface
*/
protected $ipAddress;

/**
* Constructor
*/
public function __construct(AppConfig $config, string $ipAddress)
{
$this->cookiePrefix = $config->cookiePrefix;
Expand Down Expand Up @@ -155,12 +152,11 @@ protected function releaseLock(): bool
}

/**
* Fail
*
* Drivers other than the 'files' one don't (need to) use the
* session.save_path INI setting, but that leads to confusing
* error messages emitted by PHP when open() or write() fail,
* as the message contains session.save_path ...
*
* To work around the problem, the drivers will call this method
* so that the INI is set just in time for the error message to
* be properly generated.
Expand Down
101 changes: 43 additions & 58 deletions system/Session/Handlers/DatabaseHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use CodeIgniter\Session\Exceptions\SessionException;
use Config\App as AppConfig;
use Config\Database;
use Exception;
use ReturnTypeWillChange;

/**
* Session handler using current Database for storage
Expand Down Expand Up @@ -58,27 +58,24 @@ class DatabaseHandler extends BaseHandler
protected $rowExists = false;

/**
* Constructor
* @throws SessionException
*/
public function __construct(AppConfig $config, string $ipAddress)
{
parent::__construct($config, $ipAddress);

// Determine Table
$this->table = $config->sessionSavePath;

if (empty($this->table)) {
throw SessionException::forMissingDatabaseTable();
}

// Get DB Connection
// @phpstan-ignore-next-line
$this->DBGroup = $config->sessionDBGroup ?? config(Database::class)->defaultGroup;

$this->db = Database::connect($this->DBGroup);

// Determine Database type
$driver = strtolower(get_class($this->db));

if (strpos($driver, 'mysql') !== false) {
$this->platform = 'mysql';
} elseif (strpos($driver, 'postgre') !== false) {
Expand All @@ -87,16 +84,12 @@ public function __construct(AppConfig $config, string $ipAddress)
}

/**
* Open
*
* Ensures we have an initialized database connection.
*
* @param string $savePath Path to session files' directory
* @param string $name Session cookie name
* Re-initialize existing session, or creates a new one.
*
* @throws Exception
* @param string $path The path where to store/retrieve the session
* @param string $name The session name
*/
public function open($savePath, $name): bool
public function open($path, $name): bool
{
if (empty($this->db->connID)) {
$this->db->initialize();
Expand All @@ -106,30 +99,29 @@ public function open($savePath, $name): bool
}

/**
* Read
* Reads the session data from the session storage, and returns the results.
*
* Reads session data and acquires a lock
* @param string $id The session ID
*
* @param string $sessionID Session ID
*
* @return string Serialized session data
* @return false|string Returns an encoded string of the read data.
* If nothing was read, it must return false.
*/
public function read($sessionID): string
#[ReturnTypeWillChange]
public function read($id)
{
if ($this->lockSession($sessionID) === false) {
if ($this->lockSession($id) === false) {
$this->fingerprint = md5('');

return '';
}

// Needed by write() to detect session_regenerate_id() calls
if (! isset($this->sessionID)) {
$this->sessionID = $sessionID;
$this->sessionID = $id;
}

$builder = $this->db->table($this->table)
->select($this->platform === 'postgre' ? "encode(data, 'base64') AS data" : 'data')
->where('id', $sessionID);
->where('id', $id);

if ($this->matchIP) {
$builder = $builder->where('ip_address', $this->ipAddress);
Expand Down Expand Up @@ -160,87 +152,78 @@ public function read($sessionID): string
}

/**
* Write
*
* Writes (create / update) session data
* Writes the session data to the session storage.
*
* @param string $sessionID Session ID
* @param string $sessionData Serialized session data
* @param string $id The session ID
* @param string $data The encoded session data
*/
public function write($sessionID, $sessionData): bool
public function write($id, $data): bool
{
if ($this->lock === false) {
return $this->fail();
}

// Was the ID regenerated?
if ($sessionID !== $this->sessionID) {
if ($this->sessionID !== $id) {
$this->rowExists = false;
$this->sessionID = $sessionID;
$this->sessionID = $id;
}

if ($this->rowExists === false) {
$insertData = [
'id' => $sessionID,
'id' => $id,
'ip_address' => $this->ipAddress,
'timestamp' => 'now()',
'data' => $this->platform === 'postgre' ? '\x' . bin2hex($sessionData) : $sessionData,
'data' => $this->platform === 'postgre' ? '\x' . bin2hex($data) : $data,
];

if (! $this->db->table($this->table)->insert($insertData)) {
return $this->fail();
}

$this->fingerprint = md5($sessionData);
$this->fingerprint = md5($data);
$this->rowExists = true;

return true;
}

$builder = $this->db->table($this->table)->where('id', $sessionID);
$builder = $this->db->table($this->table)->where('id', $id);

if ($this->matchIP) {
$builder = $builder->where('ip_address', $this->ipAddress);
}

$updateData = [
'timestamp' => 'now()',
];
$updateData = ['timestamp' => 'now()'];

if ($this->fingerprint !== md5($sessionData)) {
$updateData['data'] = ($this->platform === 'postgre') ? '\x' . bin2hex($sessionData) : $sessionData;
if ($this->fingerprint !== md5($data)) {
$updateData['data'] = ($this->platform === 'postgre') ? '\x' . bin2hex($data) : $data;
}

if (! $builder->update($updateData)) {
return $this->fail();
}

$this->fingerprint = md5($sessionData);
$this->fingerprint = md5($data);

return true;
}

/**
* Close
*
* Releases locks and closes file descriptor.
* Closes the current session.
*/
public function close(): bool
{
return ($this->lock && ! $this->releaseLock()) ? $this->fail() : true;
}

/**
* Destroy
*
* Destroys the current session.
* Destroys a session
*
* @param string $sessionID
* @param string $id The session ID being destroyed
*/
public function destroy($sessionID): bool
public function destroy($id): bool
{
if ($this->lock) {
$builder = $this->db->table($this->table)->where('id', $sessionID);
$builder = $this->db->table($this->table)->where('id', $id);

if ($this->matchIP) {
$builder = $builder->where('ip_address', $this->ipAddress);
Expand All @@ -261,18 +244,20 @@ public function destroy($sessionID): bool
}

/**
* Garbage Collector
* Cleans up expired sessions.
*
* Deletes expired sessions
* @param int $max_lifetime Sessions that have not updated
* for the last max_lifetime seconds will be removed.
*
* @param int $maxlifetime Maximum lifetime of sessions
* @return false|int Returns the number of deleted sessions on success, or false on failure.
*/
public function gc($maxlifetime): bool
#[ReturnTypeWillChange]
public function gc($max_lifetime)
{
$separator = $this->platform === 'postgre' ? '\'' : ' ';
$interval = implode($separator, ['', "{$maxlifetime} second", '']);
$interval = implode($separator, ['', "{$max_lifetime} second", '']);

return $this->db->table($this->table)->delete("timestamp < now() - INTERVAL {$interval}") ? true : $this->fail();
return $this->db->table($this->table)->delete("timestamp < now() - INTERVAL {$interval}") ? 1 : $this->fail();
}

/**
Expand Down
Loading