diff --git a/composer.json b/composer.json index dd8a671c..1452e48a 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,6 @@ "php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0", "laminas/laminas-eventmanager": "^3.12", "laminas/laminas-servicemanager": "^4.0", - "laminas/laminas-stdlib": "^3.18", "laminas/laminas-validator": "^3.0", "psr/simple-cache": "^3.0" }, diff --git a/composer.lock b/composer.lock index 30585b40..1aa4d84a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "8e7cfb655e35e7329c6df670af3e119e", + "content-hash": "d3523f40e180e257485bdd12c4518f40", "packages": [ { "name": "brick/varexporter", @@ -4662,7 +4662,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/psalm-baseline.xml b/psalm-baseline.xml index a56a09f9..3f44c1c2 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,31 +1,18 @@ - - - - - - - - - - - - - - - - - - - - + + ]]> + + + + + + |Traversable]]> + @@ -70,41 +57,14 @@ - - - - - - - getArrayCopy() : $container]]> - getArrayCopy() : $container]]> - - - - - getArrayCopy()]]> - - - - - - - - - - - - - - @@ -114,9 +74,6 @@ - - - @@ -127,23 +84,6 @@ - - - - - - - - - - - - - - - $ts]]> ['hops' => $hops, 'ts' => $ts]]]> @@ -221,15 +161,9 @@ - - - - - - @@ -298,7 +232,6 @@ - name]]> validatorChain]]> @@ -350,28 +283,20 @@ + + + - - - - - - - ]]> - + ]]> - - - - @@ -382,14 +307,7 @@ - - - - - - - @@ -397,19 +315,13 @@ - getMetadata('_READONLY')]]> - getMetadata('_REQUEST_ACCESS_TIME')]]> - - - - @@ -417,22 +329,14 @@ - - - - - - - - @@ -450,13 +354,11 @@ - - @@ -478,36 +380,15 @@ - getMetadata('_READONLY')]]> - getMetadata('_REQUEST_ACCESS_TIME')]]> - - - - - - - - - - - - - - - - - - - @@ -533,35 +414,23 @@ - - - - - - - - - - - - - - getArrayCopy()]]> - + + + + + + - - - @@ -569,11 +438,11 @@ - - - - - + + + + + @@ -599,12 +468,8 @@ - - - - @@ -848,9 +713,6 @@ - - - @@ -902,10 +764,6 @@ - - - - @@ -1019,16 +877,6 @@ storage['foo']['bar']]]> storage['foo']['bar']['baz']]]> - - storage['foo']['bar']]]> - storage['foo']['bar']]]> - storage['foo']['bar']['baz']]]> - - - - - - diff --git a/src/AbstractContainer.php b/src/AbstractContainer.php index 7b4efbf2..99d70051 100644 --- a/src/AbstractContainer.php +++ b/src/AbstractContainer.php @@ -5,18 +5,21 @@ namespace Laminas\Session; use ArrayIterator; +use ArrayObject; use Laminas\Session\ManagerInterface as Manager; use Laminas\Session\Storage\StorageInterface as Storage; -use Laminas\Stdlib\ArrayObject; +use ReturnTypeWillChange; use Traversable; use function array_filter; use function array_flip; use function array_keys; use function array_map; +use function assert; use function is_array; -use function is_object; use function is_scalar; +use function is_string; +use function iterator_to_array; use function preg_match; use function time; @@ -36,44 +39,36 @@ abstract class AbstractContainer extends ArrayObject { /** * Container name - * - * @var string */ - protected $name; + protected string $name; - /** @var Manager */ - protected $manager; + protected Manager $manager; /** * Default manager class to use if no manager has been provided - * - * @var string */ - protected static $managerDefaultClass = SessionManager::class; + protected static string $managerDefaultClass = SessionManager::class; /** * Default manager to use when instantiating a container without providing a ManagerInterface - * - * @var Manager */ - protected static $defaultManager; + protected static ?Manager $defaultManager = null; /** * Default value to return by reference from offsetGet * - * @var mixed + * @phpcs:disable WebimpressCodingStandard.Classes.NoNullValues.Invalid */ - private $defaultValue; + private mixed $defaultValue = null; /** * Constructor * * Provide a name ('Default' if none provided) and a ManagerInterface instance. - * - * @param null|string $name + * @throws Exception\InvalidArgumentException */ - public function __construct($name = 'Default', ?Manager $manager = null) + public function __construct(string $name = 'Default', ?Manager $manager = null) { if (! preg_match('/^[a-z0-9][a-z0-9_\\\\]+$/i', $name)) { throw new Exception\InvalidArgumentException( @@ -92,10 +87,8 @@ public function __construct($name = 'Default', ?Manager $manager = null) /** * Set the default ManagerInterface instance to use when none provided to constructor - * - * @return void */ - public static function setDefaultManager(?Manager $manager = null) + public static function setDefaultManager(?Manager $manager): void { static::$defaultManager = $manager; } @@ -105,10 +98,9 @@ public static function setDefaultManager(?Manager $manager = null) * * If none provided, instantiates one of type {@link $managerDefaultClass} * - * @return Manager * @throws Exception\InvalidArgumentException If invalid manager default class provided. */ - public static function getDefaultManager() + public static function getDefaultManager(): Manager { if (null === static::$defaultManager) { $manager = new static::$managerDefaultClass(); @@ -125,10 +117,8 @@ public static function getDefaultManager() /** * Get container name - * - * @return string */ - public function getName() + public function getName(): string { return $this->name; } @@ -136,10 +126,9 @@ public function getName() /** * Set session manager * - * @return Container * @throws Exception\InvalidArgumentException */ - protected function setManager(?Manager $manager = null) + protected function setManager(?Manager $manager = null): static { if (null === $manager) { $manager = static::getDefaultManager(); @@ -156,10 +145,8 @@ protected function setManager(?Manager $manager = null) /** * Get manager instance - * - * @return Manager */ - public function getManager() + public function getManager(): Manager { return $this->manager; } @@ -168,20 +155,16 @@ public function getManager() * Get session storage object * * Proxies to ManagerInterface::getStorage() - * - * @return Storage */ - protected function getStorage() + protected function getStorage(): Storage { return $this->getManager()->getStorage(); } /** * Create a new container object on which to act - * - * @return ArrayObject */ - protected function createContainer() + protected function createContainer(): ArrayObject { return new ArrayObject([], ArrayObject::ARRAY_AS_PROPS); } @@ -194,17 +177,18 @@ protected function createContainer() * If not, it raises an exception; otherwise, it returns the Storage * object. * - * @param bool $createContainer Whether or not to create the container for the namespace - * @return Storage|null Returns null only if $createContainer is false + * $createContainer Whether or not to create the container for the namespace + * Returns null only if $createContainer is false + * * @throws Exception\RuntimeException */ - protected function verifyNamespace($createContainer = true) + protected function verifyNamespace(bool $createContainer = true): ?Storage { $storage = $this->getStorage(); $name = $this->getName(); if (! isset($storage[$name])) { if (! $createContainer) { - return; + return null; } $storage[$name] = $this->createContainer(); } @@ -220,10 +204,9 @@ protected function verifyNamespace($createContainer = true) * * Returns true if the key has expired, false otherwise. * - * @param null|string $key - * @return bool + * @param TKey|non-empty-string $key */ - protected function expireKeys($key = null) + protected function expireKeys(?string $key = null): bool { $storage = $this->verifyNamespace(); $name = $this->getName(); @@ -250,11 +233,9 @@ protected function expireKeys($key = null) * Checks to see if the entire container has expired based on TTL setting, * or the individual key. * - * @param string $name Container name - * @param string $key Key in container to check - * @return bool + * @param TKey|non-empty-string|null $key */ - protected function expireByExpiryTime(Storage $storage, $name, $key) + protected function expireByExpiryTime(Storage $storage, string $name, ?string $key): bool { $metadata = $storage->getMetadata($name); @@ -314,11 +295,9 @@ protected function expireByExpiryTime(Storage $storage, $name, $key) * Determines whether the container or an individual key within it has * expired based on session hops * - * @param string $name - * @param string $key - * @return bool + * @param TKey|non-empty-string|null $key */ - protected function expireByHops(Storage $storage, $name, $key) + protected function expireByHops(Storage $storage, string $name, ?string $key): bool { $ts = $storage->getRequestAccessTime(); $metadata = $storage->getMetadata($name); @@ -391,15 +370,35 @@ protected function expireByHops(Storage $storage, $name, $key) return false; } + /** + * Get Offset + * + * @param TKey|non-empty-string $key + */ + public function &__get(string $key): mixed + { + return $this->offsetGet($key); + } + + /** + * Set Offset + * + * @param TKey|non-empty-string $key + * @param TValue $value + */ + public function __set(string $key, mixed $value): void + { + $this->offsetSet($key, $value); + } + /** * Store a value within the container * - * @param string $offset - * @param mixed $value - * @return void + * @param TKey|non-empty-string $offset */ - public function offsetSet($offset, $value) + public function offsetSet(mixed $offset, mixed $value): void { + assert($offset !== ''); $this->expireKeys($offset); $storage = $this->verifyNamespace(); $name = $this->getName(); @@ -409,11 +408,11 @@ public function offsetSet($offset, $value) /** * Determine if the key exists * - * @param string $key - * @return bool + * @param TKey|non-empty-string $key */ - public function offsetExists($key) + public function offsetExists(mixed $key): bool { + assert(is_string($key) && $key !== ''); // If no container exists, we can't inspect it if (null === ($storage = $this->verifyNamespace(false))) { return false; @@ -433,11 +432,11 @@ public function offsetExists($key) /** * Retrieve a specific key in the container * - * @param string $key - * @return mixed + * @param TKey|non-empty-string $key */ - public function &offsetGet($key) + public function &offsetGet(mixed $key): mixed { + assert(is_string($key) && $key !== ''); if (! $this->offsetExists($key)) { return $this->defaultValue; } @@ -450,11 +449,11 @@ public function &offsetGet($key) /** * Unset a single key in the container * - * @param string $offset - * @return void + * @param TKey|non-empty-string $offset */ - public function offsetUnset($offset) + public function offsetUnset(mixed $offset): void { + assert($offset !== ''); if (! $this->offsetExists($offset)) { return; } @@ -463,65 +462,62 @@ public function offsetUnset($offset) unset($storage[$name][$offset]); } - /** @inheritDoc */ - public function exchangeArray($input) + /** + * Exchange the array for another one. + * + * @param array|Traversable $input + * @return array + */ + public function exchangeArray(object|array $input): array { - // handle arrayobject, iterators and the like: - if (is_object($input) && ($input instanceof ArrayObject || $input instanceof \ArrayObject)) { - $input = $input->getArrayCopy(); - } - if (! is_array($input)) { - $input = (array) $input; - } + $input = is_array($input) ? $input : iterator_to_array($input); $storage = $this->verifyNamespace(); $name = $this->getName(); + /** @var array|Traversable $old */ $old = $storage[$name]; $storage[$name] = $input; - if ($old instanceof ArrayObject) { - return $old->getArrayCopy(); - } - return $old; + /** @var array $return */ + $return = is_array($old) ? $old : iterator_to_array($old); + return $return; } - /** @inheritDoc */ - public function getIterator() + /** + * Create a new iterator from an ArrayObject instance + * + * @return Traversable + */ + #[ReturnTypeWillChange] + public function getIterator(): Traversable { $this->expireKeys(); $storage = $this->getStorage(); $container = $storage[$this->getName()]; - if ($container instanceof Traversable) { - return $container; - } - - return new ArrayIterator($container); + /** @var Traversable $return */ + $return = $container instanceof Traversable ? $container : new ArrayIterator($container); + return $return; } /** * Set expiration TTL * * Set the TTL for the entire container, a single key, or a set of keys. - * - * @param int $ttl TTL in seconds - * @param string|array|null $vars - * @return Container - * @throws Exception\InvalidArgumentException */ - public function setExpirationSeconds($ttl, $vars = null) + public function setExpirationSeconds(int $ttl, string|array|null $vars = null): static { $storage = $this->getStorage(); $ts = time() + $ttl; - if (is_scalar($vars) && null !== $vars) { + if (is_scalar($vars)) { $vars = (array) $vars; } if (null === $vars) { $this->expireKeys(); // first we need to expire global key, since it can already be expired $data = ['EXPIRE' => $ts]; - } elseif (is_array($vars)) { + } else { // Cannot pass "$this" to a lambda $container = $this; @@ -534,10 +530,6 @@ public function setExpirationSeconds($ttl, $vars = null) // Create metadata array to merge in $data = ['EXPIRE_KEYS' => $expires]; - } else { - throw new Exception\InvalidArgumentException( - 'Unknown data provided as second argument to ' . __METHOD__ - ); } $storage->setMetadata( @@ -550,13 +542,8 @@ public function setExpirationSeconds($ttl, $vars = null) /** * Set expiration hops for the container, a single key, or set of keys - * - * @param int $hops - * @param null|string|array $vars - * @throws Exception\InvalidArgumentException - * @return Container */ - public function setExpirationHops($hops, $vars = null) + public function setExpirationHops(int $hops, string|array|null $vars = null): static { $storage = $this->getStorage(); $ts = $storage->getRequestAccessTime(); @@ -568,7 +555,7 @@ public function setExpirationHops($hops, $vars = null) if (null === $vars) { $this->expireKeys(); // first we need to expire global key, since it can already be expired $data = ['EXPIRE_HOPS' => ['hops' => $hops, 'ts' => $ts]]; - } elseif (is_array($vars)) { + } else { // Cannot pass "$this" to a lambda $container = $this; @@ -581,10 +568,6 @@ public function setExpirationHops($hops, $vars = null) // Create metadata array to merge in $data = ['EXPIRE_HOPS_KEYS' => $expires]; - } else { - throw new Exception\InvalidArgumentException( - 'Unknown data provided as second argument to ' . __METHOD__ - ); } $storage->setMetadata( @@ -596,11 +579,14 @@ public function setExpirationHops($hops, $vars = null) } /** @inheritDoc */ - public function getArrayCopy() + public function getArrayCopy(): array { $storage = $this->verifyNamespace(); $container = $storage[$this->getName()]; - return $container instanceof ArrayObject ? $container->getArrayCopy() : $container; + /** @var array $array */ + $array = $container instanceof ArrayObject ? $container->getArrayCopy() : $container; + + return $array; } } diff --git a/src/Container.php b/src/Container.php index 1fbd43f9..d6721ffe 100644 --- a/src/Container.php +++ b/src/Container.php @@ -4,6 +4,9 @@ namespace Laminas\Session; +use function assert; +use function is_string; + /** * Session storage container * @@ -21,11 +24,11 @@ class Container extends AbstractContainer /** * Retrieve a specific key in the container * - * @param string $key - * @return mixed + * @param TKey|non-empty-string $key */ - public function &offsetGet($key) + public function &offsetGet(mixed $key): mixed { + assert(is_string($key) && $key !== ''); $ret = null; if (! $this->offsetExists($key)) { return $ret; diff --git a/src/SessionManager.php b/src/SessionManager.php index 52f814ec..9af5606b 100644 --- a/src/SessionManager.php +++ b/src/SessionManager.php @@ -6,7 +6,6 @@ use Laminas\EventManager\Event; use Laminas\EventManager\EventManagerInterface; -use Laminas\Stdlib\ArrayUtils; use Traversable; use function array_key_exists; @@ -121,7 +120,7 @@ public function start($preserveStorage = false) $oldSessionData = $_SESSION; // convert session data to plain array that’ll be acceptable as - // ArrayUtils::merge parameter + // array_merge parameter if ($oldSessionData instanceof Storage\StorageInterface) { $oldSessionData = $oldSessionData->toArray(); } elseif ($oldSessionData instanceof Traversable) { @@ -132,14 +131,15 @@ public function start($preserveStorage = false) session_start(); if (! empty($oldSessionData) && is_array($oldSessionData)) { - $_SESSION = ArrayUtils::merge($oldSessionData, $_SESSION, true); + /** @var array $_SESSION */ + $_SESSION = array_merge($oldSessionData, $_SESSION); } $storage = $this->getStorage(); // Since session is starting, we need to potentially repopulate our // session storage - if ($storage instanceof Storage\SessionStorage && $_SESSION !== $storage) { + if ($storage instanceof Storage\SessionStorage) { if (! $preserveStorage) { $storage->fromArray($_SESSION); } diff --git a/src/Storage/AbstractSessionArrayStorage.php b/src/Storage/AbstractSessionArrayStorage.php index 5b9dd970..2040d997 100644 --- a/src/Storage/AbstractSessionArrayStorage.php +++ b/src/Storage/AbstractSessionArrayStorage.php @@ -5,18 +5,19 @@ namespace Laminas\Session\Storage; use ArrayIterator; -use ArrayObject; use IteratorAggregate; use Laminas\Session\Exception; -use ReturnTypeWillChange; +use Traversable; use function array_flip; use function array_key_exists; use function array_keys; use function array_replace_recursive; +use function assert; use function count; use function is_array; -use function is_object; +use function is_string; +use function iterator_to_array; use function microtime; use function serialize; use function sprintf; @@ -28,9 +29,7 @@ * Replaces the $_SESSION superglobal with an ArrayObject that allows for * property access, metadata storage, locking, and immutability. * - * @see ReturnTypeWillChange - * - * @template TKey of array-key + * @template TKey of string * @template TValue * @template-implements IteratorAggregate * @template-implements StorageInterface @@ -41,11 +40,9 @@ abstract class AbstractSessionArrayStorage implements StorageInitializationInterface { /** - * Constructor - * - * @param array|null $input + * @param iterable|null $input */ - public function __construct($input = null) + public function __construct(?iterable $input = null) { // this is here for B.C. $this->init($input); @@ -53,30 +50,22 @@ public function __construct($input = null) /** * Initialize Storage - * - * @param array $input - * @return void */ - public function init($input = null) + public function init(?iterable $input = null): void { - if ((null === $input) && isset($_SESSION)) { - $input = $_SESSION; - if (is_object($input) && ! $_SESSION instanceof ArrayObject) { - $input = (array) $input; - } - } elseif (null === $input) { - $input = []; - } + $input ??= $_SESSION ?? []; + $input = $input instanceof Traversable ? iterator_to_array($input) : $input; $_SESSION = $input; + $this->setRequestAccessTime(microtime(true)); } /** * Get Offset * - * @return mixed + * @param TKey|non-empty-string $key */ - public function __get(mixed $key) + public function __get(string $key): mixed { return $this->offsetGet($key); } @@ -84,9 +73,10 @@ public function __get(mixed $key) /** * Set Offset * - * @return void + * @param TKey|non-empty-string $key + * @param TValue $value */ - public function __set(mixed $key, mixed $value) + public function __set(string $key, mixed $value): void { $this->offsetSet($key, $value); } @@ -94,9 +84,9 @@ public function __set(mixed $key, mixed $value) /** * Isset Offset * - * @return bool + * @param TKey|non-empty-string $key */ - public function __isset(mixed $key) + public function __isset(string $key): bool { return $this->offsetExists($key); } @@ -104,10 +94,11 @@ public function __isset(mixed $key) /** * Unset Offset * - * @return void + * @param TKey|non-empty-string $key */ - public function __unset(mixed $key) + public function __unset(string $key): void { + assert($key !== ''); $this->offsetUnset($key); } @@ -123,64 +114,60 @@ public function __destruct() /** * Offset Exists * - * @return bool + * @param TKey|non-empty-string $key */ - #[ReturnTypeWillChange] - public function offsetExists(mixed $key) + public function offsetExists(mixed $key): bool { + assert(is_string($key) && $key !== ''); return isset($_SESSION[$key]); } /** * Offset Get * - * @return mixed + * @param TKey|non-empty-string $key */ - #[ReturnTypeWillChange] - public function offsetGet(mixed $key) + public function offsetGet(mixed $key): mixed { + assert(is_string($key) && $key !== ''); return $_SESSION[$key] ?? null; } /** * Offset Set * - * @return void + * @param TKey|non-empty-string $offset + * @param TValue $value */ - #[ReturnTypeWillChange] - public function offsetSet(mixed $offset, mixed $value) + public function offsetSet(mixed $offset, mixed $value): void { + assert(is_string($offset) && $offset !== ''); $_SESSION[$offset] = $value; } /** * Offset Unset * - * @return void + * @param TKey|non-empty-string $offset */ - #[ReturnTypeWillChange] - public function offsetUnset(mixed $offset) + public function offsetUnset(mixed $offset): void { + assert(is_string($offset) && $offset !== ''); unset($_SESSION[$offset]); } /** * Count - * - * @return int */ - #[ReturnTypeWillChange] - public function count() + public function count(): int { return count($_SESSION); } /** - * Seralize - * - * @return string + * Serialize */ - public function serialize() + public function serialize(): string { return serialize($_SESSION); } @@ -188,19 +175,23 @@ public function serialize() /** * Unserialize * - * @param string $session - * @return mixed + * @param non-empty-string $session */ - public function unserialize($session) + public function unserialize(string $session): mixed { return unserialize($session); } - /** @inheritDoc */ - #[ReturnTypeWillChange] - public function getIterator() + /** + * Retrieve an external iterator + * + * @return Traversable + */ + public function getIterator(): Traversable { - return new ArrayIterator($_SESSION); + /** @var Traversable $return */ + $return = new ArrayIterator($_SESSION); + return $return; } /** @@ -208,9 +199,12 @@ public function getIterator() * * Ensures $_SESSION is set to an instance of the object when complete. * - * @return SessionStorage + * @template TKeyIn of string + * @template TValIn + * @param array $array + * @return static */ - public function fromArray(array $array) + public function fromArray(array $array): static { $ts = $this->getRequestAccessTime(); $_SESSION = $array; @@ -221,10 +215,8 @@ public function fromArray(array $array) /** * Mark object as isImmutable - * - * @return SessionStorage */ - public function markImmutable() + public function markImmutable(): static { $_SESSION['_IMMUTABLE'] = true; @@ -233,10 +225,8 @@ public function markImmutable() /** * Determine if this object is isImmutable - * - * @return bool */ - public function isImmutable() + public function isImmutable(): bool { return isset($_SESSION['_IMMUTABLE']) && $_SESSION['_IMMUTABLE']; } @@ -244,10 +234,9 @@ public function isImmutable() /** * Lock this storage instance, or a key within it * - * @param null|int|string $key - * @return $this + * @psalm-param TKey|non-empty-string|null $key */ - public function lock($key = null) + public function lock(?string $key = null): static { if (null === $key) { $this->setMetadata('_READONLY', true); @@ -264,10 +253,9 @@ public function lock($key = null) /** * Is the object or key marked as locked? * - * @param null|int|string $key - * @return bool + * @param TKey|non-empty-string|null $key */ - public function isLocked($key = null) + public function isLocked(?string $key = null): bool { if ($this->isImmutable()) { // isImmutable trumps all @@ -301,10 +289,9 @@ public function isLocked($key = null) /** * Unlock an object or key marked as locked * - * @param null|int|string $key - * @return $this + * @param TKey|non-empty-string|null $key */ - public function unlock($key = null) + public function unlock(?string $key = null): static { if (null === $key) { // Unlock everything @@ -343,13 +330,11 @@ public function unlock($key = null) * - localizing session storage * - etc. * - * @param string $key - * @param mixed $value - * @param bool $overwriteArray Whether to overwrite or merge array values; by default, merges - * @return $this + * @param bool $overwriteArray Whether to overwrite or merge array values; by default, merges + * @param TKey|non-empty-string $key * @throws Exception\RuntimeException */ - public function setMetadata($key, $value, $overwriteArray = false) + public function setMetadata(string $key, mixed $value, bool $overwriteArray = false): static { if ($this->isImmutable()) { throw new Exception\RuntimeException( @@ -386,10 +371,9 @@ public function setMetadata($key, $value, $overwriteArray = false) * Returns false if no metadata stored, or no metadata exists for the given * key. * - * @param null|int|string $key - * @return mixed + * @param TKey|non-empty-string|null $key */ - public function getMetadata($key = null) + public function getMetadata(?string $key = null): mixed { if (! isset($_SESSION['__Laminas'])) { return false; @@ -409,11 +393,10 @@ public function getMetadata($key = null) /** * Clear the storage object or a subkey of the object * - * @param null|int|string $key - * @return $this + * @param TKey|non-empty-string|null $key * @throws Exception\RuntimeException */ - public function clear($key = null) + public function clear(?string $key = null): static { if ($this->isImmutable()) { throw new Exception\RuntimeException('Cannot clear storage as it is marked immutable'); @@ -433,21 +416,16 @@ public function clear($key = null) /** * Retrieve the request access time - * - * @return float */ - public function getRequestAccessTime() + public function getRequestAccessTime(): float { - return $this->getMetadata('_REQUEST_ACCESS_TIME'); + return (float) $this->getMetadata('_REQUEST_ACCESS_TIME'); } /** * Set the request access time - * - * @param float $time - * @return $this */ - protected function setRequestAccessTime($time) + protected function setRequestAccessTime(float $time): static { $this->setMetadata('_REQUEST_ACCESS_TIME', $time); @@ -457,18 +435,18 @@ protected function setRequestAccessTime($time) /** * Cast the object to an array * - * @param bool $metaData Whether to include metadata + * @param bool $metadata Whether to include metadata * @return array */ - public function toArray($metaData = false) + public function toArray(bool $metadata = false): array { - if (isset($_SESSION)) { - $values = $_SESSION; - } else { - $values = []; - } + /** @var iterable $values */ + $values = $_SESSION ?? []; + + /** @var array $values */ + $values = is_array($values) ? $values : iterator_to_array($values); - if ($metaData) { + if ($metadata) { return $values; } diff --git a/src/Storage/ArrayStorage.php b/src/Storage/ArrayStorage.php index 90094263..de9656d2 100644 --- a/src/Storage/ArrayStorage.php +++ b/src/Storage/ArrayStorage.php @@ -5,15 +5,16 @@ namespace Laminas\Session\Storage; use ArrayIterator; +use ArrayObject; use Laminas\Session\Exception; -use Laminas\Stdlib\ArrayObject; -use ReturnTypeWillChange; use function array_flip; use function array_key_exists; use function array_keys; use function array_replace_recursive; +use function assert; use function is_array; +use function is_string; use function microtime; use function sprintf; @@ -23,9 +24,7 @@ * Defines an ArrayObject interface for accessing session storage, with options * for setting metadata, locking, and marking as isImmutable. * - * @see ReturnTypeWillChange - * - * @template TKey of array-key + * @template TKey of string * @template TValue * @template-extends ArrayObject * @template-implements StorageInterface @@ -34,10 +33,8 @@ class ArrayStorage extends ArrayObject implements StorageInterface { /** * Is storage marked isImmutable? - * - * @var bool */ - protected $isImmutable = false; + protected bool $isImmutable = false; /** * Constructor @@ -45,14 +42,12 @@ class ArrayStorage extends ArrayObject implements StorageInterface * Instantiates storage as an ArrayObject, allowing property access. * Also sets the initial request access time. * - * @param array $input - * @param int $flags - * @param string $iteratorClass + * @param array|StorageInterface $input */ public function __construct( - $input = [], - $flags = ArrayObject::ARRAY_AS_PROPS, - $iteratorClass = ArrayIterator::class + array|StorageInterface $input = [], + int $flags = ArrayObject::ARRAY_AS_PROPS, + string $iteratorClass = ArrayIterator::class ) { parent::__construct($input, $flags, $iteratorClass); $this->setRequestAccessTime(microtime(true)); @@ -60,11 +55,8 @@ public function __construct( /** * Set the request access time - * - * @param float $time - * @return $this */ - protected function setRequestAccessTime($time) + protected function setRequestAccessTime(float $time): static { $this->setMetadata('_REQUEST_ACCESS_TIME', $time); @@ -73,12 +65,34 @@ protected function setRequestAccessTime($time) /** * Retrieve the request access time + */ + public function getRequestAccessTime(): float + { + return (float) $this->getMetadata('_REQUEST_ACCESS_TIME'); + } + + /** + * Get Offset * - * @return float + * @param TKey|non-empty-string $key */ - public function getRequestAccessTime() + public function __get(string $key): mixed { - return $this->getMetadata('_REQUEST_ACCESS_TIME'); + assert($key !== ''); + /** @psalm-var TKey $key */ + return $this->offsetGet($key); + } + + /** + * Set Offset + * + * @param TKey|non-empty-string $key + * @param TValue $value + */ + public function __set(string $key, mixed $value): void + { + assert($key !== ''); + $this->offsetSet($key, $value); } /** @@ -87,14 +101,12 @@ public function getRequestAccessTime() * If the object is marked as isImmutable, or the object or key is marked as * locked, raises an exception. * - * @param array-key $offset - * @param mixed $value - * @return void + * @param TKey|non-empty-string $offset * @throws Exception\RuntimeException */ - #[ReturnTypeWillChange] - public function offsetSet($offset, $value) + public function offsetSet(mixed $offset, mixed $value): void { + assert(is_string($offset) && $offset !== ''); if ($this->isImmutable()) { throw new Exception\RuntimeException( sprintf('Cannot set key "%s" as storage is marked isImmutable', $offset) @@ -106,22 +118,24 @@ public function offsetSet($offset, $value) ); } + /** @psalm-var TKey $offset */ parent::offsetSet($offset, $value); } /** * Lock this storage instance, or a key within it * - * @param null|int|string $key - * @return $this + * @param TKey|non-empty-string|null $key */ - public function lock($key = null) + public function lock(?string $key = null): static { if (null === $key) { $this->setMetadata('_READONLY', true); return $this; } + + /** @psalm-var TKey $key */ if (isset($this[$key])) { $this->setMetadata('_LOCKS', [$key => true]); } @@ -132,10 +146,9 @@ public function lock($key = null) /** * Is the object or key marked as locked? * - * @param null|int|string $key - * @return bool + * @param TKey|non-empty-string|null $key */ - public function isLocked($key = null) + public function isLocked(?string $key = null): bool { if ($this->isImmutable()) { // isImmutable trumps all @@ -168,10 +181,9 @@ public function isLocked($key = null) /** * Unlock an object or key marked as locked * - * @param null|int|string $key - * @return $this + * @param TKey|non-empty-string|null $key */ - public function unlock($key = null) + public function unlock(?string $key = null): static { if (null === $key) { // Unlock everything @@ -202,10 +214,8 @@ public function unlock($key = null) /** * Mark the storage container as isImmutable - * - * @return $this */ - public function markImmutable() + public function markImmutable(): static { $this->isImmutable = true; @@ -214,10 +224,8 @@ public function markImmutable() /** * Is the storage container marked as isImmutable? - * - * @return bool */ - public function isImmutable() + public function isImmutable(): bool { return $this->isImmutable; } @@ -232,13 +240,10 @@ public function isImmutable() * - localizing session storage * - etc. * - * @param string $key - * @param mixed $value - * @param bool $overwriteArray Whether to overwrite or merge array values; by default, merges - * @return $this + * @param TKey|non-empty-string $key * @throws Exception\RuntimeException */ - public function setMetadata($key, $value, $overwriteArray = false) + public function setMetadata(string $key, mixed $value, bool $overwriteArray = false): static { if ($this->isImmutable) { throw new Exception\RuntimeException( @@ -279,10 +284,9 @@ public function setMetadata($key, $value, $overwriteArray = false) * Returns false if no metadata stored, or no metadata exists for the given * key. * - * @param null|int|string $key - * @return mixed + * @param TKey|non-empty-string|null $key */ - public function getMetadata($key = null) + public function getMetadata(?string $key = null): mixed { if (! isset($this['__Laminas'])) { return false; @@ -302,11 +306,10 @@ public function getMetadata($key = null) /** * Clear the storage object or a subkey of the object * - * @param null|int|string $key - * @return $this + * @param TKey|non-empty-string|null $key * @throws Exception\RuntimeException */ - public function clear($key = null) + public function clear(?string $key = null): static { if ($this->isImmutable()) { throw new Exception\RuntimeException('Cannot clear storage as it is marked immutable'); @@ -317,6 +320,7 @@ public function clear($key = null) return $this; } + /** @psalm-var TKey $key */ if (! isset($this[$key])) { return $this; } @@ -326,7 +330,7 @@ public function clear($key = null) // Clear key metadata $this->setMetadata($key, null) - ->unlock($key); + ->unlock($key); return $this; } @@ -335,10 +339,8 @@ public function clear($key = null) * Load the storage from another array * * Overwrites any data that was previously set. - * - * @return $this */ - public function fromArray(array $array) + public function fromArray(array $array): static { $ts = $this->getRequestAccessTime(); $this->exchangeArray($array); @@ -350,13 +352,14 @@ public function fromArray(array $array) /** * Cast the object to an array * - * @param bool $metaData Whether to include metadata + * @param bool $metadata Whether to include metadata * @return array */ - public function toArray($metaData = false) + public function toArray(bool $metadata = false): array { $values = $this->getArrayCopy(); - if ($metaData) { + + if ($metadata) { return $values; } if (isset($values['__Laminas'])) { diff --git a/src/Storage/Factory.php b/src/Storage/Factory.php index 0550b128..b6a4d060 100644 --- a/src/Storage/Factory.php +++ b/src/Storage/Factory.php @@ -6,13 +6,8 @@ namespace Laminas\Session\Storage; use ArrayAccess; +use ArrayObject; use Laminas\Session\Exception; -use Laminas\Session\Storage\AbstractSessionArrayStorage; -use Laminas\Session\Storage\ArrayStorage; -use Laminas\Session\Storage\StorageInterface; -use Laminas\Stdlib\ArrayObject; -use Laminas\Stdlib\ArrayUtils; -use Traversable; use function class_exists; use function class_implements; @@ -20,7 +15,6 @@ use function get_debug_type; use function in_array; use function is_array; -use function is_string; use function sprintf; abstract class Factory @@ -28,43 +22,24 @@ abstract class Factory /** * Create and return a StorageInterface instance * - * @param string $type - * @param array|Traversable $options - * @return StorageInterface * @throws Exception\InvalidArgumentException For unrecognized $type or individual options. */ - public static function factory($type, $options = []) + public static function factory(string $type, array $options = []): StorageInterface { - if (! is_string($type)) { - throw new Exception\InvalidArgumentException(sprintf( - '%s expects the $type argument to be a string class name; received "%s"', - __METHOD__, - get_debug_type($type) - )); - } if (! class_exists($type)) { $class = __NAMESPACE__ . '\\' . $type; if (! class_exists($class)) { - throw new Exception\InvalidArgumentException(sprintf( - '%s expects the $type argument to be a valid class name; received "%s"', - __METHOD__, - $type - )); + throw new Exception\InvalidArgumentException( + sprintf( + '%s expects the $type argument to be a valid class name; received "%s"', + __METHOD__, + $type + ) + ); } $type = $class; } - if ($options instanceof Traversable) { - $options = ArrayUtils::iteratorToArray($options); - } - if (! is_array($options)) { - throw new Exception\InvalidArgumentException(sprintf( - '%s expects the $options argument to be an array or Traversable; received "%s"', - __METHOD__, - get_debug_type($options) - )); - } - switch (true) { case in_array(AbstractSessionArrayStorage::class, class_parents($type)): return static::createSessionArrayStorage($type, $options); @@ -74,22 +49,20 @@ public static function factory($type, $options = []) case in_array(StorageInterface::class, class_implements($type)): return new $type($options); default: - throw new Exception\InvalidArgumentException(sprintf( - 'Unrecognized type "%s" provided; expects a class implementing %s\StorageInterface', - $type, - __NAMESPACE__ - )); + throw new Exception\InvalidArgumentException( + sprintf( + 'Unrecognized type "%s" provided; expects a class implementing %s\StorageInterface', + $type, + __NAMESPACE__ + ) + ); } } /** * Create a storage object from an ArrayStorage class (or a descendent) - * - * @param string $type - * @param array $options - * @return ArrayStorage */ - protected static function createArrayStorage($type, $options) + protected static function createArrayStorage(string $type, array $options): ArrayStorage { $input = []; $flags = ArrayObject::ARRAY_AS_PROPS; @@ -97,11 +70,13 @@ protected static function createArrayStorage($type, $options) if (isset($options['input']) && null !== $options['input']) { if (! is_array($options['input'])) { - throw new Exception\InvalidArgumentException(sprintf( - '%s expects the "input" option to be an array; received "%s"', - $type, - get_debug_type($options['input']) - )); + throw new Exception\InvalidArgumentException( + sprintf( + '%s expects the "input" option to be an array; received "%s"', + $type, + get_debug_type($options['input']) + ) + ); } $input = $options['input']; } @@ -112,11 +87,13 @@ protected static function createArrayStorage($type, $options) if (isset($options['iterator_class'])) { if (! class_exists($options['iterator_class'])) { - throw new Exception\InvalidArgumentException(sprintf( - '%s expects the "iterator_class" option to be a valid class; received "%s"', - $type, - get_debug_type($options['iterator_class']) - )); + throw new Exception\InvalidArgumentException( + sprintf( + '%s expects the "iterator_class" option to be a valid class; received "%s"', + $type, + get_debug_type($options['iterator_class']) + ) + ); } $iteratorClass = $options['iterator_class']; } @@ -127,11 +104,9 @@ protected static function createArrayStorage($type, $options) /** * Create a storage object from a class extending AbstractSessionArrayStorage * - * @param string $type - * @return AbstractSessionArrayStorage * @throws Exception\InvalidArgumentException If the input option is invalid. */ - protected static function createSessionArrayStorage($type, array $options) + protected static function createSessionArrayStorage(string $type, array $options): AbstractSessionArrayStorage { $input = null; if (isset($options['input'])) { diff --git a/src/Storage/SessionArrayStorage.php b/src/Storage/SessionArrayStorage.php index 58aaa736..a038d538 100644 --- a/src/Storage/SessionArrayStorage.php +++ b/src/Storage/SessionArrayStorage.php @@ -4,10 +4,13 @@ namespace Laminas\Session\Storage; +use function assert; +use function is_string; + /** - * Session storage in $_SESSION' + * Session storage in $_SESSION * - * @template TKey of array-key + * @template TKey of string * @template TValue * @template-extends AbstractSessionArrayStorage */ @@ -16,22 +19,24 @@ class SessionArrayStorage extends AbstractSessionArrayStorage /** * Get Offset * - * @param mixed $key - * @return mixed + * @param TKey|non-empty-string $key */ - public function &__get($key) + public function &__get(string $key): mixed { + assert($key !== ''); + /** @psalm-var non-empty-string $key */ return $_SESSION[$key]; } /** * Offset Get * - * @param mixed $key - * @return mixed + * @param TKey|non-empty-string $key */ - public function &offsetGet($key) + public function &offsetGet(mixed $key): mixed { + assert(is_string($key) && $key !== ''); + /** @psalm-var non-empty-string $key */ return $_SESSION[$key]; } } diff --git a/src/Storage/SessionStorage.php b/src/Storage/SessionStorage.php index fe5ebd3f..5de6d38f 100644 --- a/src/Storage/SessionStorage.php +++ b/src/Storage/SessionStorage.php @@ -5,7 +5,8 @@ namespace Laminas\Session\Storage; use ArrayIterator; -use Laminas\Stdlib\ArrayObject; +use ArrayObject; +use Iterator; use function is_object; @@ -15,7 +16,7 @@ * Replaces the $_SESSION superglobal with an ArrayObject that allows for * property access, metadata storage, locking, and immutability. * - * @template TKey of array-key + * @template TKey of string * @template TValue * @template-extends ArrayStorage */ @@ -27,14 +28,13 @@ class SessionStorage extends ArrayStorage * Sets the $_SESSION superglobal to an ArrayObject, maintaining previous * values if any discovered. * - * @param array|null $input - * @param int $flags - * @param string $iteratorClass + * @param array|null $input + * @param class-string $iteratorClass */ public function __construct( - $input = null, - $flags = ArrayObject::ARRAY_AS_PROPS, - $iteratorClass = ArrayIterator::class + array|null $input = null, + int $flags = ArrayObject::ARRAY_AS_PROPS, + string $iteratorClass = ArrayIterator::class ) { $resetSession = true; if ((null === $input) && isset($_SESSION)) { @@ -64,7 +64,7 @@ public function __construct( */ public function __destruct() { - $_SESSION = (array) $this->getArrayCopy(); + $_SESSION = $this->getArrayCopy(); } /** @@ -73,9 +73,8 @@ public function __destruct() * Ensures $_SESSION is set to an instance of the object when complete. * * @param array $array - * @return $this */ - public function fromArray(array $array) + public function fromArray(array $array): static { parent::fromArray($array); if ($_SESSION !== $this) { @@ -87,10 +86,8 @@ public function fromArray(array $array) /** * Mark object as isImmutable - * - * @return $this */ - public function markImmutable() + public function markImmutable(): static { $this['_IMMUTABLE'] = true; @@ -99,10 +96,8 @@ public function markImmutable() /** * Determine if this object is isImmutable - * - * @return bool */ - public function isImmutable() + public function isImmutable(): bool { return isset($this['_IMMUTABLE']) && $this['_IMMUTABLE']; } diff --git a/src/Storage/StorageInitializationInterface.php b/src/Storage/StorageInitializationInterface.php index 6043bfa3..000af27a 100644 --- a/src/Storage/StorageInitializationInterface.php +++ b/src/Storage/StorageInitializationInterface.php @@ -15,8 +15,7 @@ interface StorageInitializationInterface /** * Initialize Session Storage * - * @param array $input - * @return void + * @param iterable|null $input */ - public function init($input = null); + public function init(?iterable $input = null): void; } diff --git a/src/Storage/StorageInterface.php b/src/Storage/StorageInterface.php index f9ec155e..85426ad7 100644 --- a/src/Storage/StorageInterface.php +++ b/src/Storage/StorageInterface.php @@ -15,68 +15,57 @@ * Defines the minimum requirements for handling userland, in-script session * storage (e.g., the $_SESSION superglobal array). * - * @template TKey of array-key + * @extends ArrayAccess + * @template TKey of string * @template TValue * @template-extends Traversable * @template-extends ArrayAccess */ interface StorageInterface extends Traversable, ArrayAccess, Serializable, Countable { - /** @return float */ - public function getRequestAccessTime(); + public function getRequestAccessTime(): float; /** - * @param null|int|string $key - * @return self + * @param TKey|non-empty-string|null $key */ - public function lock($key = null); + public function lock(?string $key = null): static; /** - * @param null|int|string $key - * @return bool + * @param TKey|non-empty-string|null $key */ - public function isLocked($key = null); + public function isLocked(?string $key = null): bool; /** - * @param null|int|string $key - * @return self + * @param TKey|non-empty-string|null $key */ - public function unlock($key = null); + public function unlock(?string $key = null): static; - /** @return self */ - public function markImmutable(); + public function markImmutable(): static; - /** @return bool */ - public function isImmutable(); + public function isImmutable(): bool; /** - * @param string $key - * @param mixed $value - * @param bool $overwriteArray - * @return self + * @param TKey|non-empty-string $key */ - public function setMetadata($key, $value, $overwriteArray = false); + public function setMetadata(string $key, mixed $value, bool $overwriteArray = false): static; /** - * @param null|int|string $key - * @return mixed + * @param TKey|non-empty-string|null $key */ - public function getMetadata($key = null); + public function getMetadata(?string $key = null): mixed; /** - * @param null|int|string $key - * @return self + * @param TKey|non-empty-string|null $key */ - public function clear($key = null); + public function clear(?string $key = null): static; /** - * @return self + * @param array $array */ - public function fromArray(array $array); + public function fromArray(array $array): static; /** - * @param bool $metadata - * @return array + * @return array */ - public function toArray($metadata = false); + public function toArray(bool $metadata = false): array; } diff --git a/test/AbstractContainerTest.php b/test/AbstractContainerTest.php index beff683a..7c1b5e37 100644 --- a/test/AbstractContainerTest.php +++ b/test/AbstractContainerTest.php @@ -4,10 +4,12 @@ namespace LaminasTest\Session; +use Laminas\Session\AbstractContainer; use Laminas\Session\Config\StandardConfig; use Laminas\Session\Container; use Laminas\Session\ManagerInterface as Manager; use LaminasTest\Session\TestAsset\TestContainer; +use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\TestCase; /** @@ -15,20 +17,8 @@ */ class AbstractContainerTest extends TestCase { - /** - * Hack to allow running tests in separate processes - * - * @see http://matthewturland.com/2010/08/19/process-isolation-in-phpunit/ - * - * @var bool - */ - protected $preserveGlobalState = false; - - /** @var Manager */ - protected $manager; - - /** @var Container */ - protected $container; + protected Manager $manager; + protected AbstractContainer $container; protected function setUp(): void { @@ -47,10 +37,7 @@ protected function tearDown(): void Container::setDefaultManager(null); } - /** - * This test case fails on laminas-session 2.8.0 with the php error below and works fine on 2.7.*. - * "Only variable references should be returned by reference" - */ + #[IgnoreDeprecations] public function testOffsetGetMissingKey(): void { self::assertNull($this->container->offsetGet('this key does not exist in the container')); diff --git a/test/ContainerTest.php b/test/ContainerTest.php index 148d2abd..aca1f8c0 100644 --- a/test/ContainerTest.php +++ b/test/ContainerTest.php @@ -11,6 +11,7 @@ use Laminas\Session\Container; use Laminas\Session\Exception\InvalidArgumentException; use Laminas\Session\ManagerInterface as Manager; +use Laminas\Session\Storage\ArrayStorage; use Laminas\Session\Storage\SessionArrayStorage; use PHPUnit\Framework\Attributes\IgnoreDeprecations; use PHPUnit\Framework\Attributes\RequiresPhp; @@ -30,15 +31,6 @@ */ class ContainerTest extends TestCase { - /** - * Hack to allow running tests in separate processes - * - * @see http://matthewturland.com/2010/08/19/process-isolation-in-phpunit/ - * - * @var bool - */ - protected $preserveGlobalState = false; - /** @var Manager */ protected $manager; @@ -558,7 +550,7 @@ public function testExchangeArrayObject(): void $this->container->offsetSet('old', 'old'); self::assertTrue($this->container->offsetExists('old')); - $old = $this->container->exchangeArray(new \Laminas\Stdlib\ArrayObject(['new' => 'new'])); + $old = $this->container->exchangeArray(new ArrayStorage(['new' => 'new'])); self::assertArrayHasKey('old', $old, "'exchangeArray' doesn't return an array of old items"); self::assertFalse($this->container->offsetExists('old'), "'exchangeArray' doesn't remove old items"); self::assertTrue($this->container->offsetExists('new'), "'exchangeArray' doesn't add the new array items"); diff --git a/test/Service/SessionManagerFactoryTest.php b/test/Service/SessionManagerFactoryTest.php index ad1483a3..a8df7447 100644 --- a/test/Service/SessionManagerFactoryTest.php +++ b/test/Service/SessionManagerFactoryTest.php @@ -143,9 +143,8 @@ public function testStartingSessionManagerFromFactoryDoesNotTriggerUndefinedVari self::assertSame($storage, $manager->getStorage()); } - /** - * @runInSeparateProcess - */ + #[RunInSeparateProcess] + #[IgnoreDeprecations] public function testFactoryDoesNotOverwriteValidatorStorageValues(): void { $storage = new ArrayStorage(); @@ -177,9 +176,8 @@ public function testFactoryDoesNotOverwriteValidatorStorageValues(): void self::assertSame('1.2.3.4', $validatorData[Validator\RemoteAddr::class]); } - /** - * @runInSeparateProcess - */ + #[RunInSeparateProcess] + #[IgnoreDeprecations] public function testFactoryDoesNotAttachValidatorTwoTimes(): void { $storage = new ArrayStorage(); diff --git a/test/Service/StorageFactoryTest.php b/test/Service/StorageFactoryTest.php index da017db6..7697c214 100644 --- a/test/Service/StorageFactoryTest.php +++ b/test/Service/StorageFactoryTest.php @@ -117,7 +117,9 @@ public function testUsesConfigurationToCreateStorage(array $config, string $clas $storage = $this->services->get(StorageInterface::class); self::assertInstanceOf($class, $storage); $test = $storage->toArray(); - self::assertEquals($config['session_storage']['options']['input'], $test); + + self::assertArrayHasKey('foo', $test); + self::assertEquals('bar', $test['foo']); } public function testConfigurationWithoutInputIsValid(): void diff --git a/test/SessionManagerTest.php b/test/SessionManagerTest.php index f1d7546c..d788cbd6 100644 --- a/test/SessionManagerTest.php +++ b/test/SessionManagerTest.php @@ -45,7 +45,6 @@ use const PHP_SAPI; /** - * @preserveGlobalState disabled * @covers \Laminas\Session\SessionManager */ class SessionManagerTest extends TestCase diff --git a/test/SessionStorageTest.php b/test/SessionStorageTest.php index bfe7cd39..6639edb5 100644 --- a/test/SessionStorageTest.php +++ b/test/SessionStorageTest.php @@ -15,13 +15,12 @@ */ class SessionStorageTest extends TestCase { - /** @var SessionStorage */ + /** @var SessionStorage $storage */ private SessionStorage $storage; protected function setUp(): void { - $_SESSION = []; - /** @var SessionStorage $sessionStorage */ + $_SESSION = []; $sessionStorage = new SessionStorage(); $this->storage = $sessionStorage; } diff --git a/test/TestAsset/Php81CompatibleStorageInterface.php b/test/TestAsset/Php81CompatibleStorageInterface.php index 510397bf..3479893e 100644 --- a/test/TestAsset/Php81CompatibleStorageInterface.php +++ b/test/TestAsset/Php81CompatibleStorageInterface.php @@ -7,7 +7,7 @@ use Laminas\Session\Storage\StorageInterface; /** - * @template TKey of array-key + * @template TKey of string * @template TValue * @template-extends StorageInterface */ diff --git a/test/Validator/CsrfTest.php b/test/Validator/CsrfTest.php index d6bfdfe1..bbdd6661 100644 --- a/test/Validator/CsrfTest.php +++ b/test/Validator/CsrfTest.php @@ -190,6 +190,7 @@ public function testSessionContainerContainsHashAfterHashHasBeenGenerated(): voi self::assertIsString($hash); $tokenId = $method->invoke($this->validator, $hash); self::assertIsString($tokenId); + self::assertIsArray($container->tokenList); $token = $container->tokenList[$tokenId] ?? ''; self::assertIsString($token);