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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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)
Comment thread
paulbalandan marked this conversation as resolved.
{
return true;
return 1;
}
}
6 changes: 1 addition & 5 deletions system/Session/Handlers/BaseHandler.php
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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)
Comment thread
paulbalandan marked this conversation as resolved.
{
return true;
return 1;
}
}
6 changes: 1 addition & 5 deletions system/Session/Handlers/BaseHandler.php
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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)
Comment thread
paulbalandan marked this conversation as resolved.
{
return true;
return 1;
}
}
6 changes: 1 addition & 5 deletions system/Session/Handlers/BaseHandler.php
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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)
Comment thread
paulbalandan marked this conversation as resolved.
{
return true;
return 1;
}
}
6 changes: 1 addition & 5 deletions system/Session/Handlers/BaseHandler.php
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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)
Comment thread
paulbalandan marked this conversation as resolved.
{
return true;
return 1;
}
}
6 changes: 1 addition & 5 deletions system/Session/Handlers/BaseHandler.php
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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)
Comment thread
paulbalandan marked this conversation as resolved.
{
return true;
return 1;
}
}
6 changes: 1 addition & 5 deletions system/Session/Handlers/BaseHandler.php
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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)
Comment thread
paulbalandan marked this conversation as resolved.
{
return true;
return 1;
}
}
6 changes: 1 addition & 5 deletions system/Session/Handlers/BaseHandler.php
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content
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 numberDiff line numberDiff 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 numberDiff line numberDiff 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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)
Comment thread
paulbalandan marked this conversation as resolved.
{
return true;
return 1;
}
}
6 changes: 1 addition & 5 deletions system/Session/Handlers/BaseHandler.php
Original file line numberDiff line numberDiff 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 DownExpand 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 numberDiff line numberDiff 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 DownExpand 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 DownExpand 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 $sessionIDSession ID
* @param string $sessionData Serialized session data
* @param string $idThe 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