diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2c0f654..e5ad299 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,7 +8,7 @@ on: jobs: tests: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 10 services: @@ -33,16 +33,16 @@ jobs: ports: - 6379:6379 - name: PHP 8.3 + name: PHP 8.5 steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup PHP uses: shivammathur/setup-php@v2 with: - php-version: 8.3 + php-version: 8.5 tools: composer coverage: xdebug @@ -83,7 +83,7 @@ jobs: php-coveralls --coverage_clover=build/logs/clover.xml -v tests-latest: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 timeout-minutes: 10 services: @@ -112,7 +112,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v6 - name: Setup PHP uses: shivammathur/setup-php@v2 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0ff2d72..3a637ee 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,5 +1,5 @@ --- -image: registry.gitlab.com/aplus-framework/images/base:4 +image: registry.gitlab.com/aplus-framework/images/base:6 include: - template: Security/SAST.gitlab-ci.yml diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index 0100ce9..eb4aad8 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -10,7 +10,7 @@ use Framework\CodingStandard\Config; use Framework\CodingStandard\Finder; -return (new Config())->setDefaultHeaderComment( +return new Config()->setDefaultHeaderComment( 'Aplus Framework Session Library', 'Natan Felles ' )->setFinder( diff --git a/composer.json b/composer.json index 7ca4c65..c3cbcd0 100644 --- a/composer.json +++ b/composer.json @@ -33,16 +33,16 @@ } ], "require": { - "php": ">=8.3", + "php": ">=8.5", "ext-memcached": "*", "ext-redis": "*", - "aplus/database": "^4.0", - "aplus/debug": "^4.0", - "aplus/log": "^4.0" + "aplus/database": "^5.0", + "aplus/debug": "^5.0", + "aplus/log": "^5.0" }, "require-dev": { "ext-xdebug": "*", - "aplus/coding-standard": "^2.8", + "aplus/coding-standard": "^3.0", "ergebnis/composer-normalize": "^2.25", "jetbrains/phpstorm-attributes": "^1.0", "phpmd/phpmd": "^2.13", diff --git a/docker-compose.yml b/docker-compose.yml index 5557e35..d6dfab9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,6 @@ -version: "3" services: package: - image: registry.gitlab.com/aplus-framework/images/package:4 + image: registry.gitlab.com/aplus-framework/images/package:6 container_name: package-session working_dir: /package volumes: diff --git a/guide/index.rst b/guide/index.rst index 258402c..57e731c 100644 --- a/guide/index.rst +++ b/guide/index.rst @@ -274,6 +274,8 @@ These are the DatabaseHandler configs: .. code-block:: php $configs = [ + // The Database configs + 'database' => [], // The name of the table used for sessions 'table' => 'Sessions', // The maxlifetime used for locking @@ -325,7 +327,7 @@ A basic example of a table for sessions is below: .. code-block:: sql CREATE TABLE `Sessions` ( - `id` varchar(128) NOT NULL, + `id` char(32) NOT NULL, `timestamp` timestamp NOT NULL, `data` blob NOT NULL, `ip` varchar(45) NOT NULL, -- optional diff --git a/src/Debug/SessionCollector.php b/src/Debug/SessionCollector.php index 1b10f50..072d1b0 100644 --- a/src/Debug/SessionCollector.php +++ b/src/Debug/SessionCollector.php @@ -358,7 +358,7 @@ protected function renderAutoRegenerateId() : string */ protected function getSaveHandlerConfigs() : array { - $config = $this->saveHandler->getConfig(); + $config = $this->saveHandler->getConfigs(); if ($this->saveHandler instanceof FilesHandler) { return [ 'Directory' => $config['directory'], diff --git a/src/SaveHandler.php b/src/SaveHandler.php index ed6a762..212ca7b 100644 --- a/src/SaveHandler.php +++ b/src/SaveHandler.php @@ -11,6 +11,7 @@ use Framework\Log\Logger; use Framework\Log\LogLevel; +use OutOfBoundsException; use SensitiveParameter; /** @@ -29,7 +30,7 @@ abstract class SaveHandler implements \SessionHandlerInterface, \SessionUpdateTi * * @var array */ - protected array $config; + protected array $configs; /** * The current data fingerprint. * @@ -74,36 +75,55 @@ abstract class SaveHandler implements \SessionHandlerInterface, \SessionUpdateTi /** * SessionSaveHandler constructor. * - * @param array $config + * @param array $configs * @param Logger|null $logger */ public function __construct( #[SensitiveParameter] - array $config = [], + array $configs = [], ?Logger $logger = null ) { - $this->prepareConfig($config); + $this->prepareConfigs($configs); $this->logger = $logger; } /** * Prepare configurations to be used by the save handler. * - * @param array $config Custom configs + * @param array $configs Custom configs * * @codeCoverageIgnore */ - protected function prepareConfig(#[SensitiveParameter] array $config) : void + protected function prepareConfigs(#[SensitiveParameter] array $configs) : void { - $this->config = $config; + $this->configs = $configs; } /** * @return array */ - public function getConfig() : array + public function getConfigs() : array { - return $this->config; + return $this->configs; + } + + /** + * Get a config item by key. + * + * @param string $key + * @param bool $nullable + * + * @return mixed + */ + public function getConfig(string $key, bool $nullable = false) : mixed + { + if (!\array_key_exists($key, $this->configs)) { + if($nullable) { + return null; + } + throw new OutOfBoundsException('Invalid config key: ' . $key); + } + return $this->configs[$key]; } /** @@ -161,22 +181,28 @@ protected function hasSameFingerprint(string $data) : bool */ protected function getMaxlifetime() : int { - return (int) ($this->config['maxlifetime'] ?? \ini_get('session.gc_maxlifetime')); + return (int) ($this->getConfig('maxlifetime', true) ?? \ini_get('session.gc_maxlifetime')); } /** * Get the remote IP address. * + * - The IP address is present in all HTTP requests. + * * @return string */ protected function getIP() : string { - return $_SERVER['REMOTE_ADDR'] ?? ''; + $key = $this->getConfig('ip_key', true) ?? 'REMOTE_ADDR'; + return $_SERVER[$key]; } /** * Get the HTTP User-Agent. * + * - Allows falling back to an empty string because the User-Agent header + * can be omitted. + * * @return string */ protected function getUA() : string @@ -187,10 +213,10 @@ protected function getUA() : string protected function getKeySuffix() : string { $suffix = ''; - if ($this->config['match_ip']) { + if ($this->getConfig('match_ip')) { $suffix .= ':' . $this->getIP(); } - if ($this->config['match_ua']) { + if ($this->getConfig('match_ua')) { $suffix .= ':' . $this->getUA(); } if ($suffix) { @@ -211,18 +237,7 @@ protected function getKeySuffix() : string */ public function validateId($id) : bool { - if (\PHP_VERSION_ID >= 80400) { - return (bool) \preg_match('#\A[0-9a-f]{32}\z#', $id); - } - $bits = \ini_get('session.sid_bits_per_character') ?: 5; - $length = \ini_get('session.sid_length') ?: 40; - $bitsRegex = [ - 4 => '[0-9a-f]', - 5 => '[0-9a-v]', - 6 => '[0-9a-zA-Z,-]', - ]; - return isset($bitsRegex[$bits]) - && \preg_match('#\A' . $bitsRegex[$bits] . '{' . $length . '}\z#', $id); + return (bool) \preg_match('#\A[0-9a-f]{32}\z#', $id); } /** diff --git a/src/SaveHandlers/DatabaseHandler.php b/src/SaveHandlers/DatabaseHandler.php index 2492370..dcd3cf7 100644 --- a/src/SaveHandlers/DatabaseHandler.php +++ b/src/SaveHandlers/DatabaseHandler.php @@ -23,7 +23,7 @@ * * ```sql * CREATE TABLE `Sessions` ( - * `id` varchar(128) NOT NULL, + * `id` char(32) NOT NULL, * `timestamp` timestamp NOT NULL, * `data` blob NOT NULL, * `ip` varchar(45) NOT NULL, -- optional @@ -35,8 +35,6 @@ * ); * ``` * - * NOTE: As of PHP 8.4 the id column can be `char(32)`. - * * @package session */ class DatabaseHandler extends SaveHandler @@ -46,12 +44,14 @@ class DatabaseHandler extends SaveHandler /** * Prepare configurations to be used by the DatabaseHandler. * - * @param array $config Custom configs + * @param array $configs Custom configs * * The custom configs are: * * ```php * $configs = [ + * // The Database configs + * 'database' => [], * // The name of the table used for sessions * 'table' => 'Sessions', * // The maxlifetime used for locking @@ -63,6 +63,7 @@ class DatabaseHandler extends SaveHandler * 'timestamp' => 'timestamp', * 'ip' => 'ip', * 'ua' => 'ua', + * 'user_id' => 'user_id', * ], * // Match IP? * 'match_ip' => false, @@ -72,14 +73,17 @@ class DatabaseHandler extends SaveHandler * 'save_ip' => false, * // Independent of match_ua, save the initial User-Agent in the ua column? * 'save_ua' => false, + * // Save the user_id? + * 'save_user_id' => false, * ]; * ``` * * NOTE: The Database::connect configs was not shown. */ - protected function prepareConfig(#[SensitiveParameter] array $config) : void + protected function prepareConfigs(#[SensitiveParameter] array $configs) : void { - $this->config = \array_replace_recursive([ + $this->configs = \array_replace_recursive([ + 'database' => [], 'table' => 'Sessions', 'maxlifetime' => null, 'columns' => [ @@ -95,7 +99,7 @@ protected function prepareConfig(#[SensitiveParameter] array $config) : void 'save_ip' => false, 'save_ua' => false, 'save_user_id' => false, - ], $config); + ], $configs); } public function setDatabase(Database $database) : static @@ -117,7 +121,7 @@ public function getDatabase() : ?Database */ protected function getTable() : string { - return $this->config['table']; + return $this->getConfig('table'); } /** @@ -129,7 +133,7 @@ protected function getTable() : string */ protected function getColumn(string $key) : string { - return $this->config['columns'][$key]; + return $this->getConfig('columns')[$key]; } /** @@ -139,10 +143,10 @@ protected function getColumn(string $key) : string */ protected function addWhereMatchs(Delete | Select | Update $statement) : void { - if ($this->config['match_ip']) { + if ($this->getConfig('match_ip')) { $statement->whereEqual($this->getColumn('ip'), $this->getIP()); } - if ($this->config['match_ua']) { + if ($this->getConfig('match_ua')) { $statement->whereEqual($this->getColumn('ua'), $this->getUA()); } } @@ -154,7 +158,7 @@ protected function addWhereMatchs(Delete | Select | Update $statement) : void */ protected function addUserIdColumn(array &$columns) : void { - if ($this->config['save_user_id']) { + if ($this->getConfig('save_user_id')) { $key = $this->getColumn('user_id'); $columns[$key] = $_SESSION[$key] ?? null; } @@ -163,7 +167,7 @@ protected function addUserIdColumn(array &$columns) : void public function open($path, $name) : bool { try { - $this->database ??= new Database($this->config); + $this->database ??= new Database($this->getConfig('database')); return true; } catch (\Exception $exception) { $this->log( @@ -190,7 +194,8 @@ public function read($id) : string $this->addWhereMatchs($statement); $row = $statement->limit(1)->run()->fetch(); $this->sessionExists = (bool) $row; - $data = $row->data ?? ''; + $column = $this->getColumn('data'); + $data = $row->{$column} ?? ''; $this->setFingerprint($data); return $data; } @@ -222,10 +227,10 @@ protected function writeInsert(string $id, string $data) : bool }, $this->getColumn('data') => $data, ]; - if ($this->config['match_ip'] || $this->config['save_ip']) { + if ($this->getConfig('match_ip') || $this->getConfig('save_ip')) { $columns[$this->getColumn('ip')] = $this->getIP(); } - if ($this->config['match_ua'] || $this->config['save_ua']) { + if ($this->getConfig('match_ua') || $this->getConfig('save_ua')) { $columns[$this->getColumn('ua')] = $this->getUA(); } $this->addUserIdColumn($columns); @@ -307,7 +312,7 @@ public function destroy($id) : bool public function gc($max_lifetime) : false | int { try { - $this->database ??= new Database($this->config); + $this->database ??= new Database($this->getConfig('database')); } catch (\Exception $exception) { $this->log( 'Session (database): Thrown a ' . \get_class($exception) diff --git a/src/SaveHandlers/FilesHandler.php b/src/SaveHandlers/FilesHandler.php index f9fe8c5..c04bb55 100644 --- a/src/SaveHandlers/FilesHandler.php +++ b/src/SaveHandlers/FilesHandler.php @@ -30,7 +30,7 @@ class FilesHandler extends SaveHandler /** * Prepare configurations to be used by the FilesHandler. * - * @param array $config Custom configs + * @param array $configs Custom configs * * The custom configs are: * @@ -47,34 +47,34 @@ class FilesHandler extends SaveHandler * ]; * ``` */ - protected function prepareConfig(#[SensitiveParameter] array $config) : void + protected function prepareConfigs(#[SensitiveParameter] array $configs) : void { - $this->config = \array_replace([ + $this->configs = \array_replace([ 'prefix' => '', 'directory' => '', 'match_ip' => false, 'match_ua' => false, - ], $config); - if (empty($this->config['directory'])) { + ], $configs); + if (empty($this->getConfig('directory'))) { throw new LogicException('Session config has not a directory'); } - $this->config['directory'] = \rtrim( - $this->config['directory'], + $this->configs['directory'] = \rtrim( + $this->getConfig('directory'), \DIRECTORY_SEPARATOR ) . \DIRECTORY_SEPARATOR; - if (!\is_dir($this->config['directory'])) { + if (!\is_dir($this->getConfig('directory'))) { throw new LogicException( - 'Session config directory does not exist: ' . $this->config['directory'] + 'Session config directory does not exist: ' . $this->getConfig('directory') ); } - if ($this->config['prefix']) { - $dirname = $this->config['directory'] . $this->config['prefix'] . \DIRECTORY_SEPARATOR; + if ($this->getConfig('prefix')) { + $dirname = $this->getConfig('directory') . $this->getConfig('prefix') . \DIRECTORY_SEPARATOR; if (!\is_dir($dirname) && !\mkdir($dirname, 0700) && !\is_dir($dirname)) { throw new RuntimeException( "Session prefix directory '{$dirname}' was not created", ); } - $this->config['directory'] = $dirname; + $this->configs['directory'] = $dirname; } } @@ -88,7 +88,7 @@ protected function prepareConfig(#[SensitiveParameter] array $config) : void */ protected function getFilename(string $id) : string { - $filename = $this->config['directory'] . $id[0] . $id[1] . \DIRECTORY_SEPARATOR . $id; + $filename = $this->getConfig('directory') . $id[0] . $id[1] . \DIRECTORY_SEPARATOR . $id; return $filename . $this->getKeySuffix(); } @@ -189,10 +189,10 @@ public function destroy($id) : bool public function gc($max_lifetime) : false | int { - $dirHandle = \opendir($this->config['directory']); + $dirHandle = \opendir($this->getConfig('directory')); if ($dirHandle === false) { $this->log( - "Session (files): Garbage Collector could not open directory '{$this->config['directory']}'", + "Session (files): Garbage Collector could not open directory '{$this->getConfig('directory')}'", LogLevel::DEBUG ); return false; @@ -202,10 +202,10 @@ public function gc($max_lifetime) : false | int while (($filename = \readdir($dirHandle)) !== false) { if ($filename !== '.' && $filename !== '..' - && \is_dir($this->config['directory'] . $filename) + && \is_dir($this->getConfig('directory') . $filename) ) { $gcCount += $this->gcSubdir( - $this->config['directory'] . $filename, + $this->getConfig('directory') . $filename, $max_lifetime ); } diff --git a/src/SaveHandlers/MemcachedHandler.php b/src/SaveHandlers/MemcachedHandler.php index 08444b1..ec1b3d9 100644 --- a/src/SaveHandlers/MemcachedHandler.php +++ b/src/SaveHandlers/MemcachedHandler.php @@ -27,7 +27,7 @@ class MemcachedHandler extends SaveHandler /** * Prepare configurations to be used by the MemcachedHandler. * - * @param array $config Custom configs + * @param array $configs Custom configs * * The custom configs are: * @@ -62,9 +62,9 @@ class MemcachedHandler extends SaveHandler * ]; * ``` */ - protected function prepareConfig(#[SensitiveParameter] array $config) : void + protected function prepareConfigs(#[SensitiveParameter] array $configs) : void { - $this->config = \array_replace_recursive([ + $this->configs = \array_replace_recursive([ 'prefix' => '', 'servers' => [ [ @@ -82,8 +82,8 @@ protected function prepareConfig(#[SensitiveParameter] array $config) : void 'maxlifetime' => null, 'match_ip' => false, 'match_ua' => false, - ], $config); - foreach ($this->config['servers'] as $index => $server) { + ], $configs); + foreach ($this->getConfig('servers') as $index => $server) { if (!isset($server['host'])) { throw new OutOfBoundsException( "Memcached host not set on server config '{$index}'" @@ -132,7 +132,7 @@ protected function getExpiration(int $seconds) : int */ protected function getKey(string $id) : string { - return $this->config['prefix'] . $id . $this->getKeySuffix(); + return $this->getConfig('prefix') . $id . $this->getKeySuffix(); } public function open($path, $name) : bool @@ -142,7 +142,7 @@ public function open($path, $name) : bool } $this->memcached = new Memcached(); $pool = []; - foreach ($this->config['servers'] as $server) { + foreach ($this->getConfig('servers') as $server) { $host = $server['host'] . ':' . ($server['port'] ?? 11211); if (\in_array($host, $pool, true)) { $this->log( @@ -162,7 +162,7 @@ public function open($path, $name) : bool } $pool[] = $host; } - $result = $this->memcached->setOptions($this->config['options']); + $result = $this->memcached->setOptions($this->getConfig('options')); if ($result === false) { $this->log('Session (memcached): ' . $this->memcached->getLastErrorMessage()); } @@ -204,7 +204,7 @@ public function write($id, $data) : bool $this->memcached->replace( $this->lockId, \time(), - $this->getExpiration($this->config['lock_ttl']) + $this->getExpiration($this->getConfig('lock_ttl')) ); $maxlifetime = $this->getExpiration($this->getMaxlifetime()); if ($this->hasSameFingerprint($data)) { @@ -256,16 +256,16 @@ public function gc($max_lifetime) : false | int protected function lock(string $id) : bool { - $expiration = $this->getExpiration($this->config['lock_ttl']); + $expiration = $this->getExpiration($this->getConfig('lock_ttl')); if ($this->lockId && $this->memcached->get($this->lockId)) { return $this->memcached->replace($this->lockId, \time(), $expiration); } $lockId = $this->getKey($id) . ':lock'; $attempt = 0; - while ($attempt < $this->config['lock_attempts']) { + while ($attempt < $this->getConfig('lock_attempts')) { $attempt++; if ($this->memcached->get($lockId)) { - \usleep($this->config['lock_sleep']); + \usleep($this->getConfig('lock_sleep')); continue; } if (!$this->memcached->set($lockId, \time(), $expiration)) { @@ -275,7 +275,7 @@ protected function lock(string $id) : bool $this->lockId = $lockId; break; } - if ($attempt === $this->config['lock_attempts']) { + if ($attempt === $this->getConfig('lock_attempts')) { $this->log( "Session (memcached): Unable to lock {$lockId} after {$attempt} attempts" ); diff --git a/src/SaveHandlers/RedisHandler.php b/src/SaveHandlers/RedisHandler.php index 8ac9beb..77e9918 100644 --- a/src/SaveHandlers/RedisHandler.php +++ b/src/SaveHandlers/RedisHandler.php @@ -27,7 +27,7 @@ class RedisHandler extends SaveHandler /** * Prepare configurations to be used by the RedisHandler. * - * @param array $config Custom configs + * @param array $configs Custom configs * * The custom configs are: * @@ -60,9 +60,9 @@ class RedisHandler extends SaveHandler * ]; * ``` */ - protected function prepareConfig(#[SensitiveParameter] array $config) : void + protected function prepareConfigs(#[SensitiveParameter] array $configs) : void { - $this->config = \array_replace([ + $this->configs = \array_replace([ 'prefix' => '', 'host' => '127.0.0.1', 'port' => 6379, @@ -75,7 +75,7 @@ protected function prepareConfig(#[SensitiveParameter] array $config) : void 'maxlifetime' => null, 'match_ip' => false, 'match_ua' => false, - ], $config); + ], $configs); } public function setRedis(Redis $redis) : static @@ -100,7 +100,7 @@ public function getRedis() : ?Redis */ protected function getKey(string $id) : string { - return $this->config['prefix'] . $id . $this->getKeySuffix(); + return $this->getConfig('prefix') . $id . $this->getKeySuffix(); } public function open($path, $name) : bool @@ -111,30 +111,30 @@ public function open($path, $name) : bool $this->redis = new Redis(); try { @$this->redis->connect( - $this->config['host'], - $this->config['port'], - $this->config['timeout'] + $this->getConfig('host'), + $this->getConfig('port'), + $this->getConfig('timeout') ); } catch (RedisException) { $this->log( 'Session (redis): Could not connect to server ' - . $this->config['host'] . ':' . $this->config['port'] + . $this->getConfig('host') . ':' . $this->getConfig('port') ); return false; } - if (isset($this->config['password'])) { + if ($this->getConfig('password', true) !== null) { try { - $this->redis->auth($this->config['password']); + $this->redis->auth($this->getConfig('password')); } catch (RedisException) { $this->log('Session (redis): Authentication failed'); return false; } } - if (isset($this->config['database']) - && !$this->redis->select($this->config['database']) + if ($this->getConfig('database', true) !== null + && !$this->redis->select($this->getConfig('database')) ) { $this->log( - "Session (redis): Could not select the database '{$this->config['database']}'" + "Session (redis): Could not select the database '{$this->getConfig('database')}'" ); return false; } @@ -171,7 +171,7 @@ public function write($id, $data) : bool return false; } $maxlifetime = $this->getMaxlifetime(); - $this->redis->expire($this->lockId, $this->config['lock_ttl']); + $this->redis->expire($this->lockId, $this->getConfig('lock_ttl')); if ($this->sessionExists === false || !$this->hasSameFingerprint($data)) { if ($this->redis->set($this->getKey($id), $data, $maxlifetime)) { $this->setFingerprint($data); @@ -233,17 +233,17 @@ public function gc($max_lifetime) : false | int protected function lock(string $id) : bool { - $ttl = $this->config['lock_ttl']; + $ttl = $this->getConfig('lock_ttl'); if ($this->lockId && $this->redis->get($this->lockId)) { return $this->redis->expire($this->lockId, $ttl); } $lockId = $this->getKey($id) . ':lock'; $attempt = 0; - while ($attempt < $this->config['lock_attempts']) { + while ($attempt < $this->getConfig('lock_attempts')) { $attempt++; $oldTtl = $this->redis->ttl($lockId); if (\is_int($oldTtl) && $oldTtl > 0) { - \usleep($this->config['lock_sleep']); + \usleep($this->getConfig('lock_sleep')); continue; } if (!$this->redis->setex($lockId, $ttl, (string) \time())) { @@ -253,7 +253,7 @@ protected function lock(string $id) : bool $this->lockId = $lockId; break; } - if ($attempt === $this->config['lock_attempts']) { + if ($attempt === $this->getConfig('lock_attempts')) { $this->log( "Session (redis): Unable to lock {$lockId} after {$attempt} attempts" ); diff --git a/tests/Debug/SessionCollectorTest.php b/tests/Debug/SessionCollectorTest.php index 486cada..425a65f 100644 --- a/tests/Debug/SessionCollectorTest.php +++ b/tests/Debug/SessionCollectorTest.php @@ -177,7 +177,8 @@ public function testSaveHandlers(SaveHandler $handler) : void public function testCustomSaveHandlers() : void { - $handler = new class() extends SaveHandler { + $handler = new class() extends SaveHandler + { public function open($path, $name) : bool { return true; @@ -263,14 +264,16 @@ public static function saveHandlerProvider() : Generator ]), ]; $config = [ - 'username' => \getenv('DB_USERNAME'), - 'password' => \getenv('DB_PASSWORD'), - 'schema' => \getenv('DB_SCHEMA'), - 'host' => \getenv('DB_HOST'), - 'port' => \getenv('DB_PORT'), + 'database' => [ + 'username' => \getenv('DB_USERNAME'), + 'password' => \getenv('DB_PASSWORD'), + 'schema' => \getenv('DB_SCHEMA'), + 'host' => \getenv('DB_HOST'), + 'port' => \getenv('DB_PORT'), + ], 'table' => \getenv('DB_TABLE'), ]; - $database = new Database($config); + $database = new Database($config['database']); $database->dropTable($config['table'])->ifExists()->run(); // @phpstan-ignore-line // @phpstan-ignore-next-line $database->createTable($config['table']) diff --git a/tests/SaveHandlers/AbstractHandler.php b/tests/SaveHandlers/AbstractHandler.php index 58ee232..01ed3c3 100644 --- a/tests/SaveHandlers/AbstractHandler.php +++ b/tests/SaveHandlers/AbstractHandler.php @@ -54,34 +54,11 @@ public function testValidateId() : void $id5 = 'iimuf8lvdectatt5jtkve15831funl8rg5cg6okp'; $id4 = '96aa2c863140e0e714a603cf44b0afc9a0632592'; $this->session->stop(); - /* - * Since PHP 8.4 session.sid_bits_per_character and session.sid_length - * are deprecated. - * - * - The default value for session.sid_bits_per_character is 4 (0-9, a-f). - * - The default value for session.sid_length is 32 character. - */ - if (\PHP_VERSION_ID >= 80400) { - self::assertFalse($this->handler->validateId($id6)); - self::assertFalse($this->handler->validateId($id5)); - self::assertFalse($this->handler->validateId($id4)); - $idPhp84 = '96aa2c863140e0e714a603cf44b0afc9'; - self::assertTrue($this->handler->validateId($idPhp84)); - return; - } - \ini_set('session.sid_bits_per_character', '6'); - \ini_set('session.sid_length', '40'); - self::assertTrue($this->handler->validateId($id6)); - self::assertTrue($this->handler->validateId($id5)); - self::assertTrue($this->handler->validateId($id4)); - \ini_set('session.sid_bits_per_character', '5'); - self::assertFalse($this->handler->validateId($id6)); - self::assertTrue($this->handler->validateId($id5)); - self::assertTrue($this->handler->validateId($id4)); - \ini_set('session.sid_bits_per_character', '4'); self::assertFalse($this->handler->validateId($id6)); self::assertFalse($this->handler->validateId($id5)); - self::assertTrue($this->handler->validateId($id4)); + self::assertFalse($this->handler->validateId($id4)); + $idPhp84 = '96aa2c863140e0e714a603cf44b0afc9'; + self::assertTrue($this->handler->validateId($idPhp84)); } public function testGC() : void @@ -109,4 +86,18 @@ public function testReset() : void { self::assertTrue($this->session->reset()); } + + public function testGetConfig() : void + { + if (isset($this->config['match_ip'])) { + self::assertSame( + $this->config['match_ip'], + $this->handler->getConfig('match_ip') + ); + } + self::assertNull($this->handler->getConfig('foo', true)); + $this->expectException(\OutOfBoundsException::class); + $this->expectExceptionMessage('Invalid config key: foo'); + $this->handler->getConfig('foo'); + } } diff --git a/tests/SaveHandlers/DatabaseHandlerTest.php b/tests/SaveHandlers/DatabaseHandlerTest.php index 9deaba9..693508d 100644 --- a/tests/SaveHandlers/DatabaseHandlerTest.php +++ b/tests/SaveHandlers/DatabaseHandlerTest.php @@ -26,11 +26,13 @@ class DatabaseHandlerTest extends AbstractHandler public function setUp() : void { $this->replaceConfig([ - 'username' => \getenv('DB_USERNAME'), - 'password' => \getenv('DB_PASSWORD'), - 'schema' => \getenv('DB_SCHEMA'), - 'host' => \getenv('DB_HOST'), - 'port' => \getenv('DB_PORT'), + 'database' => [ + 'username' => \getenv('DB_USERNAME'), + 'password' => \getenv('DB_PASSWORD'), + 'schema' => \getenv('DB_SCHEMA'), + 'host' => \getenv('DB_HOST'), + 'port' => \getenv('DB_PORT'), + ], 'table' => \getenv('DB_TABLE'), ]); $this->createDummyData(); @@ -39,7 +41,7 @@ public function setUp() : void protected function createDummyData() : void { - $database = new Database($this->config); + $database = new Database($this->config['database']); $database->dropTable($this->config['table'])->ifExists()->run(); $database->createTable($this->config['table']) ->definition(static function (TableDefinition $definition) : void { @@ -62,7 +64,8 @@ public function testUserId() : void $this->replaceConfig([ 'save_user_id' => true, ]); - $handler = new class($this->config, $this->logger) extends DatabaseHandler { + $handler = new class($this->config, $this->logger) extends DatabaseHandler + { public ?Database $database; }; $session = new Session(handler: $handler); @@ -83,9 +86,11 @@ public function testOpenError() : void { $this->session->stop(); $handler = new DatabaseHandler([ - 'username' => 'user-error', - 'password' => \getenv('DB_PASSWORD'), - 'host' => \getenv('DB_HOST'), + 'database' => [ + 'username' => 'user-error', + 'password' => \getenv('DB_PASSWORD'), + 'host' => \getenv('DB_HOST'), + ], ], $this->logger); $session = new Session([], $handler); $this->expectException(\RuntimeException::class); @@ -104,7 +109,8 @@ public function testOpenError() : void public function testFailToRead() : void { - $handler = new class($this->config) extends DatabaseHandler { + $handler = new class($this->config) extends DatabaseHandler + { public ?Database $database; }; $handler->database = null; @@ -113,13 +119,14 @@ public function testFailToRead() : void public function testFailToWrite() : void { - $handler = new class($this->config) extends DatabaseHandler { + $handler = new class($this->config) extends DatabaseHandler + { public ?Database $database; public false | string $lockId; }; $handler->database = null; self::assertFalse($handler->write('foo', 'data')); - $handler->database = new Database($this->config); + $handler->database = new Database($this->config['database']); $handler->lockId = false; self::assertFalse($handler->write('foo', 'data')); } @@ -136,7 +143,8 @@ public function testFailToGC() : void public function testUnlockWithoutLockId() : void { - $handler = new class() extends DatabaseHandler { + $handler = new class() extends DatabaseHandler + { public function unlock() : bool { return parent::unlock(); @@ -147,7 +155,8 @@ public function unlock() : bool public function testFailToUnlock() : void { - $handler = new class($this->config) extends DatabaseHandler { + $handler = new class($this->config) extends DatabaseHandler + { public false | string $lockId; public function unlock() : bool @@ -162,7 +171,8 @@ public function unlock() : bool public function testFailToLock() : void { - $handler = new class($this->config, $this->logger) extends DatabaseHandler { + $handler = new class($this->config, $this->logger) extends DatabaseHandler + { public function lock(string $id) : bool { return parent::lock($id); diff --git a/tests/SaveHandlers/FilesHandlerTest.php b/tests/SaveHandlers/FilesHandlerTest.php index ebbced2..fbbe438 100644 --- a/tests/SaveHandlers/FilesHandlerTest.php +++ b/tests/SaveHandlers/FilesHandlerTest.php @@ -54,7 +54,8 @@ public function testPrefix() : void 'prefix' => 'foo', 'directory' => \getenv('FILES_DIR'), ]; - $handler = new class($config) extends FilesHandler { + $handler = new class($config) extends FilesHandler + { public function getFilename(string $id) : string { return parent::getFilename($id); @@ -68,7 +69,8 @@ public function getFilename(string $id) : string public function testFailToWrite() : void { - $handler = new class($this->config) extends FilesHandler { + $handler = new class($this->config) extends FilesHandler + { public $stream; }; $handler->stream = null; @@ -77,7 +79,8 @@ public function testFailToWrite() : void public function testUnlockWithoutStream() : void { - $handler = new class($this->config) extends FilesHandler { + $handler = new class($this->config) extends FilesHandler + { public $stream; public function unlock() : bool diff --git a/tests/SaveHandlers/MemcachedHandlerTest.php b/tests/SaveHandlers/MemcachedHandlerTest.php index 7669821..deab99c 100644 --- a/tests/SaveHandlers/MemcachedHandlerTest.php +++ b/tests/SaveHandlers/MemcachedHandlerTest.php @@ -116,7 +116,8 @@ public function testInvalidOption() : void public function testFailToRead() : void { - $handler = new class($this->config) extends MemcachedHandler { + $handler = new class($this->config) extends MemcachedHandler + { public ?Memcached $memcached; }; $handler->memcached = null; @@ -125,7 +126,8 @@ public function testFailToRead() : void public function testFailToWrite() : void { - $handler = new class($this->config) extends MemcachedHandler { + $handler = new class($this->config) extends MemcachedHandler + { public ?Memcached $memcached; public false | string $lockId; public ?string $sessionId; @@ -143,7 +145,8 @@ public function testFailToWrite() : void public function testUnlocked() : void { - $handler = new class($this->config) extends MemcachedHandler { + $handler = new class($this->config) extends MemcachedHandler + { public false | string $lockId; public function unlock() : bool @@ -158,7 +161,8 @@ public function unlock() : bool public function testReplaceLock() : void { - $handler = new class($this->config, $this->logger) extends MemcachedHandler { + $handler = new class($this->config, $this->logger) extends MemcachedHandler + { public ?Memcached $memcached; public false | string $lockId; @@ -175,7 +179,8 @@ public function lock(string $id) : bool public function testFailToDestroy() : void { - $handler = new class($this->config) extends MemcachedHandler { + $handler = new class($this->config) extends MemcachedHandler + { public false | string $lockId; }; $handler->lockId = false; diff --git a/tests/SaveHandlers/RedisHandlerTest.php b/tests/SaveHandlers/RedisHandlerTest.php index b8eec6e..8de490b 100644 --- a/tests/SaveHandlers/RedisHandlerTest.php +++ b/tests/SaveHandlers/RedisHandlerTest.php @@ -70,7 +70,8 @@ public function testFailToConnectWithDatabase() : void public function testFailToRead() : void { - $handler = new class($this->config) extends RedisHandler { + $handler = new class($this->config) extends RedisHandler + { public ?Redis $redis; }; $handler->redis = null; @@ -79,7 +80,8 @@ public function testFailToRead() : void public function testFailToWrite() : void { - $handler = new class($this->config) extends RedisHandler { + $handler = new class($this->config) extends RedisHandler + { public ?Redis $redis; public ?string $sessionId; @@ -103,7 +105,8 @@ public function testFailToDestroy() : void public function testFailToClose() : void { - $handler = new class($this->config) extends RedisHandler { + $handler = new class($this->config) extends RedisHandler + { public ?Redis $redis; }; $handler->open('', ''); @@ -118,7 +121,8 @@ public function testFailToClose() : void public function testUnlock() : void { - $handler = new class($this->config, $this->logger) extends RedisHandler { + $handler = new class($this->config, $this->logger) extends RedisHandler + { public false | string $lockId; public function unlock() : bool