From 9e5f0062c199ffd678d87762aba8987ef0d2e345 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Thu, 13 Jun 2024 17:30:51 +0200 Subject: [PATCH] refactor: provide `Capabilities` as a read-only class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also removes `capability` event and the `marker` object. Capabilities are meant to be read-only and thus won't change after retrieval via `StorageInterface#getCapabilties`. By having the event deleted, there is no need to have the adapter injected, though we also removed `Capabilities#getAdapter` and the `StorageInterface` constructor argument. By refactoring the `Serializer` plugin, which already replaced the initial instance of `Capabilities`, we were able to drop the `base capabilities` feature as well. Final result is that we now provide all capabilities as public `read-only` properties which reflect their defaults which were previously handled via internal `$default` magic in `Capabilties#getCapability`. Along all the refactoring, some capabilities have changed or were removed while respecting ideas of #8 which was around since 2016: - `staticTtl` got removed without a replacement. It outlined if the cache backend is handling cache expiry or if the implementation is taking care of it. Though it might be interesting for picking a specific backend, that is rather not of interest at runtime for upstream projects. - `lockOnExpire` got removed without a replacement. Was only used in zend-server adapter which is already abandoned since 2022 - `minTtl` got renamed to `ttlSupported` and its type changed from `int` to `bool` - `maxTtl` got removed without a replacement. Every cache backend which is supported by laminas right now does allow TTLs being an infinite amount of seconds (where maximum `int` range is the limit, depending on the CPU architecture). There was a backend `XCache` where a limit existed but that backend was abandoned in 2021. - `useRequestTime` was renamed to `usesRequestTime` - `namespaceSeparator` got removed without a replacement. It was reflecting the option value and this the storage options can be used instead. Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- docs/book/v4/storage/adapter.md | 95 ++-- docs/book/v4/storage/capabilities.md | 197 +------- psalm-baseline.xml | 75 --- .../CacheItemPool/CacheItemPoolDecorator.php | 11 +- src/Psr/MaximumKeyLengthTrait.php | 2 +- src/Psr/SerializationTrait.php | 2 +- src/Psr/SimpleCache/SimpleCacheDecorator.php | 2 +- src/Storage/Adapter/AbstractAdapter.php | 15 +- src/Storage/Capabilities.php | 430 ++---------------- src/Storage/Plugin/Serializer.php | 52 +-- .../CacheItemPoolDecoratorTest.php | 72 ++- .../SimpleCache/SimpleCacheDecoratorTest.php | 100 ++-- test/Storage/Adapter/AbstractAdapterTest.php | 3 +- test/Storage/CapabilitiesTest.php | 99 ---- .../EventsCapableStorageInterface.php | 12 - 15 files changed, 198 insertions(+), 969 deletions(-) delete mode 100644 test/Storage/CapabilitiesTest.php delete mode 100644 test/Storage/TestAsset/EventsCapableStorageInterface.php diff --git a/docs/book/v4/storage/adapter.md b/docs/book/v4/storage/adapter.md index 8ec7de20..3359fdb6 100644 --- a/docs/book/v4/storage/adapter.md +++ b/docs/book/v4/storage/adapter.md @@ -515,15 +515,11 @@ This adapter implements the following interfaces: | Capability | Value | |----------------------|---------------------------------------------------------------------------------------| | `supportedDatatypes` | `null`, `bool`, `int`, `float`, `string`, `array` (serialized), `object` (serialized) | -| `minTtl` | 1 | -| `maxTtl` | 0 | -| `staticTtl` | `true` | -| `ttlPrecision` | 1 | -| `useRequestTime` | value of `apc.use_request_time` from `php.ini` | -| `lockOnExpire` | 0 | -| `maxKeyLength` | 5182 | +| `ttlSupported` | `true` | +| `ttlPrecision` | `1` | +| `usesRequestTime` | value of `apc.use_request_time` INI value, disabled by default. | +| `maxKeyLength` | `5182` | | `namespaceIsPrefix` | `true` | -| `namespaceSeparator` | Option value of `namespace_separator` | ### Metadata @@ -570,15 +566,11 @@ This adapter implements the following interfaces: | Capability | Value | |----------------------|-------------------------------------------------------------| | `supportedDatatypes` | `null`, `bool`, `int`, `float`, `string`, `array`, `object` | -| `minTtl` | 1 | -| `maxTtl` | 0 | -| `staticTtl` | `false` or `true`, depending on `psr` option | -| `ttlPrecision` | 1 | -| `useRequestTime` | false | -| `lockOnExpire` | 0 | -| `maxKeyLength` | -1 | +| `ttlSupported` | `true` | +| `ttlPrecision` | `1` | +| `usesRequestTime` | `false` | +| `maxKeyLength` | unlimited as nothing will be cached anyways | | `namespaceIsPrefix` | `true` | -| `namespaceSeparator` | none | ## Filesystem Adapter @@ -602,15 +594,11 @@ This adapter implements the following interfaces: | Capability | Value | |----------------------|--------------------------------------------------------------------------------------------------| | `supportedDatatypes` | `string`, `null` => `string`, `boolean` => `string`, `integer` => `string`, `double` => `string` | -| `minTtl` | 1 | -| `maxTtl` | 0 | -| `staticTtl` | `false` | -| `ttlPrecision` | 1 | -| `useRequestTime` | `false` | -| `lockOnExpire` | 0 | -| `maxKeyLength` | 251 | +| `ttlSupported` | `true` | +| `ttlPrecision` | `1` | +| `usesRequestTime` | `false` | +| `maxKeyLength` | `251` | | `namespaceIsPrefix` | `true` | -| `namespaceSeparator` | Option value of `namespace_separator` | ### Metadata @@ -661,15 +649,11 @@ This adapter implements the following interfaces: | Capability | Value | |----------------------|-----------------------------------------------------------------------------------------------| | `supportedDatatypes` | `null`, `boolean`, `integer`, `double`, `string`, `array` (serialized), `object` (serialized) | -| `minTtl` | 1 | -| `maxTtl` | 0 | -| `staticTtl` | `true` | -| `ttlPrecision` | 1 | -| `useRequestTime` | `false` | -| `lockOnExpire` | 0 | -| `maxKeyLength` | 255 | +| `ttlSupported` | `true` | +| `ttlPrecision` | `1` | +| `usesRequestTime` | `false` | +| `maxKeyLength` | `255` | | `namespaceIsPrefix` | `true` | -| `namespaceSeparator` | none | ### Adapter Specific Options @@ -695,15 +679,11 @@ This adapter implements the following interfaces: | Capability | Value | |----------------------|-------------------------------------------------------| | `supportedDatatypes` | `string`, `array` (serialized), `object` (serialized) | -| `minTtl` | 1 | -| `maxTtl` | 0 | -| `staticTtl` | `true` | -| `ttlPrecision` | 1 | -| `useRequestTime` | `false` | -| `lockOnExpire` | 0 | -| `maxKeyLength` | 512000000 (in Redis v3+, 255 otherwise) | +| `ttlSupported` | `true` | +| `ttlPrecision` | `1` | +| `usesRequestTime` | `false` | +| `maxKeyLength` | `512000000` (in Redis v3+, `255` in older versions) | | `namespaceIsPrefix` | `true` | -| `namespaceSeparator` | none | ### Metadata @@ -745,15 +725,11 @@ This adapter implements the following interfaces: | Capability | Value | |----------------------|-------------------------------------------------------| | `supportedDatatypes` | `string`, `array` (serialized), `object` (serialized) | -| `minTtl` | 1 | -| `maxTtl` | 0 | -| `staticTtl` | `true` | -| `ttlPrecision` | 1 | -| `useRequestTime` | `false` | -| `lockOnExpire` | 0 | -| `maxKeyLength` | 512000000 (in Redis v3+, 255 otherwise) | +| `ttlSupported` | `true` | +| `ttlPrecision` | `1` | +| `usesRequestTime` | `false` | +| `maxKeyLength` | `512000000` (in Redis v3+, `255` in older versions) | | `namespaceIsPrefix` | `true` | -| `namespaceSeparator` | none | ### Metadata @@ -796,13 +772,10 @@ This adapter implements the following interfaces: | Capability | Value | |----------------------|---------------------------------------------------------------------------------| | `supportedDatatypes` | `string`, `null`, `boolean`, `integer`, `double`, `array`, `object`, `resource` | -| `minTtl` | 1 | -| `maxTtl` | Value of `PHP_INT_MAX` | -| `staticTtl` | `false` | -| `ttlPrecision` | 0.05 | -| `useRequestTime` | `false` | -| `lockOnExpire` | 0 | -| `maxKeyLength` | 0 | +| `ttlSupported` | `true` | +| `ttlPrecision` | `0.05` | +| `usesRequestTime` | `false` | +| `maxKeyLength` | `0` | | `namespaceIsPrefix` | `false` | ### Metadata @@ -851,15 +824,11 @@ This adapter implements the following interfaces: | Capability | Value | |----------------------|-----------------------------------------------------------| | `supportedDatatypes` | `string`, `null`, `boolean`, `integer`, `double`, `array` | -| `minTtl` | 0 | -| `maxTtl` | 0 | -| `staticTtl` | `true` | -| `ttlPrecision` | 1 | -| `useRequestTime` | `false` | -| `lockOnExpire` | 0 | -| `maxKeyLength` | 255 | +| `ttlSupported` | `true` | +| `ttlPrecision` | `1` | +| `usesRequestTime` | `false` | +| `maxKeyLength` | `255` | | `namespaceIsPrefix` | `true` | -| `namespaceSeparator` | *Option value of `namespace_separator`* | ### Metadata diff --git a/docs/book/v4/storage/capabilities.md b/docs/book/v4/storage/capabilities.md index 6d1c8e92..0db2a237 100644 --- a/docs/book/v4/storage/capabilities.md +++ b/docs/book/v4/storage/capabilities.md @@ -24,156 +24,32 @@ use stdClass; use Laminas\Cache\Exception; use Laminas\EventManager\EventsCapableInterface; -class Capabilities +final class Capabilities { /** - * Constructor - * + * @param int<-1,max> $maxKeyLength + * @param SupportedDataTypesArrayShape $supportedDataTypes */ public function __construct( - StorageInterface $storage, - stdClass $marker, - array $capabilities = [], - Capabilities|null $baseCapabilities = null - ); - - /** - * Get the storage adapter - */ - public function getAdapter(): StorageInterface; - - /** - * Get supported datatypes - */ - public function getSupportedDatatypes(): array; - - /** - * Set supported datatypes - * - * @param stdClass $marker - * @param array $datatypes - * @throws Exception\InvalidArgumentException - * @return Capabilities Fluent interface - */ - public function setSupportedDatatypes(stdClass $marker, array $datatypes); - - /** - * Get minimum supported time-to-live - * - * @return int 0 means items never expire - */ - public function getMinTtl(): int; - - /** - * Set minimum supported time-to-live - * - * @param stdClass $marker - * @param int $minTtl - * @throws Exception\InvalidArgumentException - */ - public function setMinTtl(stdClass $marker, int $minTtl): self; - - /** - * Get maximum supported time-to-live - * - * @return int 0 means infinite - */ - public function getMaxTtl(): int; - - /** - * Set maximum supported time-to-live - * - * @throws Exception\InvalidArgumentException - */ - public function setMaxTtl(stdClass $marker, int $maxTtl): self; - - /** - * Is the time-to-live handled static (on write) - * or dynamic (on read) - */ - public function getStaticTtl(): bool; - - /** - * Set if the time-to-live handled static (on write) or dynamic (on read) - */ - public function setStaticTtl(stdClass $marker, bool $flag): self; - - /** - * Get time-to-live precision - */ - public function getTtlPrecision(): float; - - /** - * Set time-to-live precision - * - * @throws Exception\InvalidArgumentException - */ - public function setTtlPrecision(stdClass $marker, float $ttlPrecision): self; - - /** - * Get use request time - */ - public function getUseRequestTime(): bool; - - /** - * Set use request time - */ - public function setUseRequestTime(stdClass $marker, bool $flag): self; - - - /** - * Get "lock-on-expire" support in seconds. - * - * @return int 0 = Expired items will never be retrieved - * >0 = Time in seconds an expired item could be retrieved - * -1 = Expired items could be retrieved forever - */ - public function getLockOnExpire(): int - { - return $this->getCapability('lockOnExpire', 0); - } - - /** - * Set "lock-on-expire" support in seconds. - */ - public function setLockOnExpire(stdClass $marker, int $timeout): self - { - return $this->setCapability($marker, 'lockOnExpire', (int) $timeout); + /** + * Maximum supported key length for the cache backend + */ + public readonly int $maxKeyLength, + /** + * Whether the cache backend supports TTL + */ + public readonly bool $ttlSupported, + public readonly bool $namespaceIsPrefix, + /** + * Contains the supported data types. + * Depending on the cache backend in use, the type remains as is, is converted to a different type or is not + * supported at all. + */ + public readonly array $supportedDataTypes, + public readonly int|float $ttlPrecision, + public readonly bool $usesRequestTime, + ) { } - - /** - * Get maximum key length - * - * @return int -1 means unknown, 0 means infinite - */ - public function getMaxKeyLength(): int; - - /** - * Set maximum key length - * - * @throws Exception\InvalidArgumentException - */ - public function setMaxKeyLength(stdClass $marker, int $maxKeyLength): self; - - /** - * Get if namespace support is implemented as prefix - */ - public function getNamespaceIsPrefix(): bool; - - /** - * Set if namespace support is implemented as prefix - */ - public function setNamespaceIsPrefix(stdClass $marker, bool $flag): self; - - /** - * Get namespace separator if namespace is implemented as prefix - */ - public function getNamespaceSeparator(): string; - - /** - * Set the namespace separator if namespace is implemented as prefix - */ - public function setNamespaceSeparator(stdClass $marker, string $separator): self; } ``` @@ -192,37 +68,12 @@ $container = null; // can be any configured PSR-11 container $storageFactory = $container->get(StorageAdapterFactoryInterface::class); $cache = $storageFactory->create('filesystem'); -$supportedDatatypes = $cache->getCapabilities()->getSupportedDatatypes(); +$supportedDataTypes = $cache->getCapabilities()->supportedDataTypes; // now you can run specific stuff in base of supported feature -if ($supportedDatatypes['object']) { +if ($supportedDataTypes['object']) { $cache->set($key, $object); } else { $cache->set($key, serialize($object)); } -``` - -### Listen to the change Event - -```php -use Laminas\Cache\Service\StorageAdapterFactoryInterface; -use Psr\Container\ContainerInterface; - -/** @var ContainerInterface $container */ -$container = null; // can be any configured PSR-11 container - -/** @var StorageAdapterFactoryInterface $storageFactory */ -$storageFactory = $container->get(StorageAdapterFactoryInterface::class); - -$cache = $storageFactory->create('filesystem', [ - 'no_atime' => false, -]); - -// Catching capability changes -$cache->getEventManager()->attach('capability', function($event) { - echo count($event->getParams()) . ' capabilities changed'; -}); - -// change option which changes capabilities -$cache->getOptions()->setNoATime(true); -``` +``` \ No newline at end of file diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 22806fa1..cf1ddf8f 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -265,73 +265,6 @@ - - - - - - - - - - - - - - - - - - getCapability('lockOnExpire', 0)]]> - getCapability('maxKeyLength', self::UNKNOWN_KEY_LENGTH)]]> - getCapability('maxTtl', 0)]]> - getCapability('minTtl', 0)]]> - getCapability('namespaceIsPrefix', true)]]> - getCapability('namespaceSeparator', '')]]> - getCapability('staticTtl', false)]]> - getCapability('supportedDatatypes', [ - 'NULL' => false, - 'boolean' => false, - 'integer' => false, - 'double' => false, - 'string' => true, - 'array' => false, - 'object' => false, - 'resource' => false, - ])]]> - getCapability('ttlPrecision', 1)]]> - getCapability('useRequestTime', false)]]> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -383,9 +316,6 @@ - - - getAdapter()]]> @@ -397,9 +327,6 @@ - - - @@ -547,11 +474,9 @@ - - diff --git a/src/Psr/CacheItemPool/CacheItemPoolDecorator.php b/src/Psr/CacheItemPool/CacheItemPoolDecorator.php index de48d1ca..768bd518 100644 --- a/src/Psr/CacheItemPool/CacheItemPoolDecorator.php +++ b/src/Psr/CacheItemPool/CacheItemPoolDecorator.php @@ -307,26 +307,19 @@ private function validateStorage(StorageInterface $storage): void // we've got to be able to set per-item TTL on write $capabilities = $storage->getCapabilities(); - if (! ($capabilities->getStaticTtl() && $capabilities->getMinTtl())) { + if (! $capabilities->ttlSupported) { throw new CacheException(sprintf( 'Storage %s does not support static TTL', $storage::class )); } - if ($capabilities->getUseRequestTime()) { + if ($capabilities->usesRequestTime) { throw new CacheException(sprintf( 'The capability "use-request-time" of storage %s violates PSR-6', $storage::class )); } - - if ($capabilities->getLockOnExpire()) { - throw new CacheException(sprintf( - 'The capability "lock-on-expire" of storage %s violates PSR-6', - $storage::class - )); - } } /** diff --git a/src/Psr/MaximumKeyLengthTrait.php b/src/Psr/MaximumKeyLengthTrait.php index 37371e10..18058dc4 100644 --- a/src/Psr/MaximumKeyLengthTrait.php +++ b/src/Psr/MaximumKeyLengthTrait.php @@ -37,7 +37,7 @@ trait MaximumKeyLengthTrait private function memoizeMaximumKeyLengthCapability(StorageInterface $storage, Capabilities $capabilities): void { - $maximumKeyLength = $capabilities->getMaxKeyLength(); + $maximumKeyLength = $capabilities->maxKeyLength; if ($maximumKeyLength === Capabilities::UNLIMITED_KEY_LENGTH) { $this->maximumKeyLength = Capabilities::UNLIMITED_KEY_LENGTH; diff --git a/src/Psr/SerializationTrait.php b/src/Psr/SerializationTrait.php index c6541bb8..150f4627 100644 --- a/src/Psr/SerializationTrait.php +++ b/src/Psr/SerializationTrait.php @@ -21,7 +21,7 @@ private function isSerializationRequired(StorageInterface $storage): bool { $capabilities = $storage->getCapabilities(); $requiredTypes = ['string', 'integer', 'double', 'boolean', 'NULL', 'array', 'object']; - $types = $capabilities->getSupportedDatatypes(); + $types = $capabilities->supportedDataTypes; foreach ($requiredTypes as $type) { // 'object' => 'object' is OK diff --git a/src/Psr/SimpleCache/SimpleCacheDecorator.php b/src/Psr/SimpleCache/SimpleCacheDecorator.php index 1f206edb..3751b531 100644 --- a/src/Psr/SimpleCache/SimpleCacheDecorator.php +++ b/src/Psr/SimpleCache/SimpleCacheDecorator.php @@ -361,7 +361,7 @@ private function assertValidKey(string|int $key): void */ private function memoizeTtlCapabilities(Capabilities $capabilities): void { - $this->providesPerItemTtl = $capabilities->getStaticTtl() && (0 < $capabilities->getMinTtl()); + $this->providesPerItemTtl = 0 < $capabilities->ttlSupported; } /** diff --git a/src/Storage/Adapter/AbstractAdapter.php b/src/Storage/Adapter/AbstractAdapter.php index f073c0dc..8daa1488 100644 --- a/src/Storage/Adapter/AbstractAdapter.php +++ b/src/Storage/Adapter/AbstractAdapter.php @@ -16,7 +16,6 @@ use Laminas\EventManager\EventManagerInterface; use Laminas\EventManager\ResponseCollection; use SplObjectStorage; -use stdClass; use Throwable; use Webmozart\Assert\Assert; @@ -56,11 +55,6 @@ abstract class AbstractAdapter implements StorageInterface, PluginAwareInterface */ protected ?Capabilities $capabilities = null; - /** - * Marker to change capabilities - */ - protected ?object $capabilityMarker = null; - /** * options * @@ -1180,7 +1174,7 @@ public function getCapabilities(): Capabilities Assert::isInstanceOf($result, Capabilities::class); return $result; } catch (Throwable $throwable) { - $result = $this->triggerThrowable(__FUNCTION__, $args, new Capabilities($this, new stdClass()), $throwable); + $result = $this->triggerThrowable(__FUNCTION__, $args, new Capabilities(), $throwable); Assert::isInstanceOf($result, Capabilities::class); return $result; @@ -1192,12 +1186,7 @@ public function getCapabilities(): Capabilities */ protected function internalGetCapabilities(): Capabilities { - if ($this->capabilities === null) { - $this->capabilityMarker = new stdClass(); - $this->capabilities = new Capabilities($this, $this->capabilityMarker); - } - - return $this->capabilities; + return $this->capabilities ??= new Capabilities(); } /* internal */ diff --git a/src/Storage/Capabilities.php b/src/Storage/Capabilities.php index ce2d1b27..ba7e5bc7 100644 --- a/src/Storage/Capabilities.php +++ b/src/Storage/Capabilities.php @@ -2,404 +2,56 @@ namespace Laminas\Cache\Storage; -use ArrayObject; -use Laminas\Cache\Exception; -use Laminas\EventManager\EventsCapableInterface; -use stdClass; - -use function array_diff; -use function array_keys; -use function in_array; -use function is_string; -use function strtolower; - -class Capabilities +/** + * @psalm-type DataTypeConversionType = 'null'|'boolean'|'integer'|'double'|'string'|'array'|'object'|'resource' + * @psalm-type SupportedDataTypesArrayShape = array{ + * 'NULL'?: bool|DataTypeConversionType, + * 'boolean'?: bool|DataTypeConversionType, + * 'integer'?: bool|DataTypeConversionType, + * 'double'?: bool|DataTypeConversionType, + * 'string'?: bool|DataTypeConversionType, + * 'array'?: bool|DataTypeConversionType, + * 'object'?: bool|DataTypeConversionType, + * 'resource'?: bool|DataTypeConversionType, + * } + */ +final class Capabilities { public const UNKNOWN_KEY_LENGTH = -1; public const UNLIMITED_KEY_LENGTH = 0; + private const DEFAULT_DATA_TYPES = [ + 'NULL' => false, + 'boolean' => false, + 'integer' => false, + 'double' => false, + 'string' => true, + 'array' => false, + 'object' => false, + 'resource' => false, + ]; /** - * "lock-on-expire" support in seconds. - * - * 0 = Expired items will never be retrieved - * >0 = Time in seconds an expired item could be retrieved - * -1 = Expired items could be retrieved forever - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. - */ - protected ?bool $lockOnExpire = null; - - /** - * Max. key length - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. - */ - protected ?int $maxKeyLength = null; - - /** - * Min. TTL (0 means items never expire) - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. - */ - protected ?int $minTtl = null; - - /** - * Max. TTL (0 means infinite) - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. - */ - protected ?int $maxTtl = null; - - /** - * Namespace is prefix - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. - */ - protected ?bool $namespaceIsPrefix = null; - - /** - * Namespace separator - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. + * @param int<-1,max> $maxKeyLength + * @param SupportedDataTypesArrayShape $supportedDataTypes */ - protected ?string $namespaceSeparator = null; - - /** - * Static ttl - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. - */ - protected ?bool $staticTtl = null; - - /** - * Supported datatypes - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. - * - * @var null|array - */ - protected ?array $supportedDatatypes = null; - - /** - * TTL precision - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. - */ - protected ?int $ttlPrecision = null; - - /** - * Use request time - * - * If it's NULL the capability isn't set and the getter - * returns the base capability or the default value. - */ - protected ?bool $useRequestTime = null; - public function __construct( - protected StorageInterface $storage, /** - * A marker to set/change capabilities + * Maximum supported key length for the cache backend + */ + public readonly int $maxKeyLength = self::UNKNOWN_KEY_LENGTH, + /** + * Whether the cache backend supports TTL + */ + public readonly bool $ttlSupported = false, + public readonly bool $namespaceIsPrefix = true, + /** + * Contains the supported data types. + * Depending on the cache backend in use, the type remains as is, is converted to a different type or is not + * supported at all. */ - protected stdClass $marker, - array $capabilities = [], - protected ?Capabilities $baseCapabilities = null + public readonly array $supportedDataTypes = self::DEFAULT_DATA_TYPES, + public readonly int|float $ttlPrecision = 1, + public readonly bool $usesRequestTime = false, ) { - foreach ($capabilities as $name => $value) { - $this->setCapability($marker, $name, $value); - } - } - - /** - * Get the storage adapter - */ - public function getAdapter(): StorageInterface - { - return $this->storage; - } - - /** - * Get supported datatypes - */ - public function getSupportedDatatypes(): array - { - return $this->getCapability('supportedDatatypes', [ - 'NULL' => false, - 'boolean' => false, - 'integer' => false, - 'double' => false, - 'string' => true, - 'array' => false, - 'object' => false, - 'resource' => false, - ]); - } - - /** - * Set supported datatypes - * - * @throws Exception\InvalidArgumentException - */ - public function setSupportedDatatypes(stdClass $marker, array $datatypes): self - { - $allTypes = [ - 'array', - 'boolean', - 'double', - 'integer', - 'NULL', - 'object', - 'resource', - 'string', - ]; - - // check/normalize datatype values - $normalized = []; - foreach ($datatypes as $type => $toType) { - if (! in_array($type, $allTypes)) { - throw new Exception\InvalidArgumentException("Unknown datatype '{$type}'"); - } - - if (is_string($toType)) { - $toType = strtolower($toType); - if (! in_array($toType, $allTypes)) { - throw new Exception\InvalidArgumentException("Unknown datatype '{$toType}'"); - } - } else { - $toType = (bool) $toType; - } - - $normalized[$type] = $toType; - } - - // add missing datatypes as not supported - $missingTypes = array_diff($allTypes, array_keys($normalized)); - foreach ($missingTypes as $type) { - $normalized[$type] = false; - } - - return $this->setCapability($marker, 'supportedDatatypes', $normalized); - } - - /** - * Get minimum supported time-to-live - * - * @return int 0 means items never expire - */ - public function getMinTtl(): int - { - return $this->getCapability('minTtl', 0); - } - - /** - * Set minimum supported time-to-live - * - * @throws Exception\InvalidArgumentException - */ - public function setMinTtl(stdClass $marker, int $minTtl): self - { - if ($minTtl < 0) { - throw new Exception\InvalidArgumentException('$minTtl must be greater or equal 0'); - } - return $this->setCapability($marker, 'minTtl', $minTtl); - } - - /** - * Get maximum supported time-to-live - * - * @return int 0 means infinite - */ - public function getMaxTtl(): int - { - return $this->getCapability('maxTtl', 0); - } - - /** - * Set maximum supported time-to-live - * - * @throws Exception\InvalidArgumentException - */ - public function setMaxTtl(stdClass $marker, int $maxTtl): self - { - if ($maxTtl < 0) { - throw new Exception\InvalidArgumentException('$maxTtl must be greater or equal 0'); - } - return $this->setCapability($marker, 'maxTtl', $maxTtl); - } - - /** - * Is the time-to-live handled static (on write) - * or dynamic (on read) - */ - public function getStaticTtl(): bool - { - return $this->getCapability('staticTtl', false); - } - - /** - * Set if the time-to-live handled static (on write) or dynamic (on read) - */ - public function setStaticTtl(stdClass $marker, bool $flag): self - { - return $this->setCapability($marker, 'staticTtl', $flag); - } - - /** - * Get time-to-live precision - */ - public function getTtlPrecision(): float - { - return $this->getCapability('ttlPrecision', 1); - } - - /** - * Set time-to-live precision - * - * @throws Exception\InvalidArgumentException - */ - public function setTtlPrecision(stdClass $marker, float $ttlPrecision): self - { - if ($ttlPrecision <= 0) { - throw new Exception\InvalidArgumentException('$ttlPrecision must be greater than 0'); - } - return $this->setCapability($marker, 'ttlPrecision', $ttlPrecision); - } - - /** - * Get use request time - */ - public function getUseRequestTime(): bool - { - return $this->getCapability('useRequestTime', false); - } - - /** - * Set use request time - */ - public function setUseRequestTime(stdClass $marker, bool $flag): self - { - return $this->setCapability($marker, 'useRequestTime', $flag); - } - - /** - * Get "lock-on-expire" support in seconds. - * - * @return int 0 = Expired items will never be retrieved - * >0 = Time in seconds an expired item could be retrieved - * -1 = Expired items could be retrieved forever - */ - public function getLockOnExpire(): int - { - return $this->getCapability('lockOnExpire', 0); - } - - /** - * Set "lock-on-expire" support in seconds. - */ - public function setLockOnExpire(stdClass $marker, int $timeout): self - { - return $this->setCapability($marker, 'lockOnExpire', $timeout); - } - - /** - * Get maximum key length - * - * @return int -1 means unknown, 0 means infinite - */ - public function getMaxKeyLength(): int - { - return $this->getCapability('maxKeyLength', self::UNKNOWN_KEY_LENGTH); - } - - /** - * Set maximum key length - * - * @throws Exception\InvalidArgumentException - */ - public function setMaxKeyLength(stdClass $marker, int $maxKeyLength): self - { - if ($maxKeyLength < -1) { - throw new Exception\InvalidArgumentException('$maxKeyLength must be greater or equal than -1'); - } - return $this->setCapability($marker, 'maxKeyLength', $maxKeyLength); - } - - /** - * Get if namespace support is implemented as prefix - */ - public function getNamespaceIsPrefix(): bool - { - return $this->getCapability('namespaceIsPrefix', true); - } - - /** - * Set if namespace support is implemented as prefix - */ - public function setNamespaceIsPrefix(stdClass $marker, bool $flag): self - { - return $this->setCapability($marker, 'namespaceIsPrefix', $flag); - } - - /** - * Get namespace separator if namespace is implemented as prefix - */ - public function getNamespaceSeparator(): string - { - return $this->getCapability('namespaceSeparator', ''); - } - - /** - * Set the namespace separator if namespace is implemented as prefix - */ - public function setNamespaceSeparator(stdClass $marker, string $separator): self - { - return $this->setCapability($marker, 'namespaceSeparator', $separator); - } - - /** - * Get a capability - */ - protected function getCapability(string $property, mixed $default = null): mixed - { - if ($this->$property !== null) { - return $this->$property; - } elseif ($this->baseCapabilities) { - $getMethod = 'get' . $property; - return $this->baseCapabilities->$getMethod(); - } - return $default; - } - - /** - * Change a capability - * - * @throws Exception\InvalidArgumentException - */ - protected function setCapability(stdClass $marker, string $property, mixed $value): self - { - if ($this->marker !== $marker) { - throw new Exception\InvalidArgumentException('Invalid marker'); - } - - if ($this->$property !== $value) { - $this->$property = $value; - - // trigger event - if ($this->storage instanceof EventsCapableInterface) { - $this->storage->getEventManager()->trigger('capability', $this->storage, new ArrayObject([ - $property => $value, - ])); - } - } - - return $this; } } diff --git a/src/Storage/Plugin/Serializer.php b/src/Storage/Plugin/Serializer.php index 2cecd1f4..0ef6a6cb 100644 --- a/src/Storage/Plugin/Serializer.php +++ b/src/Storage/Plugin/Serializer.php @@ -8,15 +8,11 @@ use Laminas\EventManager\EventManagerInterface; use Laminas\Serializer\Adapter\AdapterInterface; use Laminas\ServiceManager\PluginManagerInterface; -use stdClass; use function assert; -use function spl_object_hash; final class Serializer extends AbstractPlugin { - protected array $capabilities = []; - private ?AdapterInterface $serializer = null; /** @@ -111,34 +107,32 @@ public function onWriteItemsPre(Event $event): void } /** - * On get capabilities + * Update data types when using serializer plugin. */ public function onGetCapabilitiesPost(PostEvent $event): void { - $baseCapabilities = $event->getResult(); - $index = spl_object_hash($baseCapabilities); - - if (! isset($this->capabilities[$index])) { - $this->capabilities[$index] = new Capabilities( - $baseCapabilities->getAdapter(), - new stdClass(), // marker - [ - 'supportedDatatypes' => [ - 'NULL' => true, - 'boolean' => true, - 'integer' => true, - 'double' => true, - 'string' => true, - 'array' => true, - 'object' => 'object', - 'resource' => false, - ], - ], - $baseCapabilities - ); - } - - $event->setResult($this->capabilities[$index]); + $capabilities = $event->getResult(); + assert($capabilities instanceof Capabilities); + + $capabilitiesWithUpdatedDataTypes = new Capabilities( + $capabilities->maxKeyLength, + $capabilities->ttlSupported, + $capabilities->namespaceIsPrefix, + [ + 'NULL' => true, + 'boolean' => true, + 'integer' => true, + 'double' => true, + 'string' => true, + 'array' => true, + 'object' => 'object', + 'resource' => false, + ], + $capabilities->ttlPrecision, + $capabilities->usesRequestTime, + ); + + $event->setResult($capabilitiesWithUpdatedDataTypes); } public function getSerializer(): AdapterInterface diff --git a/test/Psr/CacheItemPool/CacheItemPoolDecoratorTest.php b/test/Psr/CacheItemPool/CacheItemPoolDecoratorTest.php index 4cdb710b..73c56915 100644 --- a/test/Psr/CacheItemPool/CacheItemPoolDecoratorTest.php +++ b/test/Psr/CacheItemPool/CacheItemPoolDecoratorTest.php @@ -16,14 +16,12 @@ use Laminas\Cache\Storage\ClearByNamespaceInterface; use Laminas\Cache\Storage\FlushableInterface; use Laminas\Cache\Storage\StorageInterface; -use Laminas\EventManager\EventManager; use LaminasTest\Cache\Psr\CacheItemPool\TestAsset\FlushableStorageAdapterInterface; use LaminasTest\Cache\Psr\TestAsset\FlushableNamespaceStorageInterface; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Cache\CacheItemInterface; use Psr\Clock\ClockInterface; -use stdClass; use Throwable; use function array_keys; @@ -34,15 +32,17 @@ use function str_repeat; use function time; +/** + * @psalm-import-type SupportedDataTypesArrayShape from Capabilities + */ final class CacheItemPoolDecoratorTest extends TestCase { /** @var StorageInterface&FlushableInterface&MockObject */ - private $storage; + private StorageInterface&FlushableInterface&MockObject $storage; private ?CacheItemPoolDecorator $adapter; - /** @var array */ - private array $requiredTypes = [ + private const REQUIRED_TYPES = [ 'NULL' => true, 'boolean' => true, 'integer' => true, @@ -53,8 +53,7 @@ final class CacheItemPoolDecoratorTest extends TestCase 'resource' => false, ]; - /** @var AdapterOptions&MockObject */ - private $options; + private AdapterOptions&MockObject $options; protected function setUp(): void { @@ -65,31 +64,24 @@ protected function setUp(): void } /** + * @param SupportedDataTypesArrayShape|null $supportedDataTypes + * @param int<-1,max> $maxKeyLength * @return StorageInterface&FlushableInterface&ClearByNamespaceInterface&MockObject */ private function createMockedStorage( ?AdapterOptions $options = null, ?array $supportedDataTypes = null, - bool $staticTtl = true, - int $minTtl = 1, + bool $ttlSupported = true, int $maxKeyLength = -1, bool $useRequestTime = false, - bool $lockOnExpire = false ): StorageInterface { $storage = $this->createMock(FlushableNamespaceStorageInterface::class); - $storage - ->method('getEventManager') - ->willReturn(new EventManager()); - $capabilities = $this->createCapabilities( - $storage, $supportedDataTypes, - $staticTtl, - $minTtl, + $ttlSupported, $maxKeyLength, $useRequestTime, - $lockOnExpire ); $storage @@ -108,7 +100,7 @@ public function testStorageNeedsSerializerWillThrowException(): void $this->expectException(CacheException::class); $storage = $this->createMock(FlushableStorageAdapterInterface::class); - $capabilities = $this->createCapabilities($storage, [ + $capabilities = $this->createCapabilities(supportedDataTypes: [ 'NULL' => true, 'boolean' => true, 'integer' => true, @@ -127,17 +119,10 @@ public function testStorageNeedsSerializerWillThrowException(): void $this->getAdapter($storage); } - public function testStorageFalseStaticTtlThrowsException(): void + public function testBackendDoesNotSupportTtlThrowsException(): void { $this->expectException(CacheException::class); - $storage = $this->createMockedStorage(null, null, false); - $this->getAdapter($storage); - } - - public function testStorageZeroMinTtlThrowsException(): void - { - $this->expectException(CacheException::class); - $storage = $this->createMockedStorage(null, null, true, 0); + $storage = $this->createMockedStorage(null, null, ttlSupported: false); $this->getAdapter($storage); } @@ -930,7 +915,7 @@ public function testWontSaveAlreadyExpiredCacheItemAsDeferredItem(): void $adapter ->expects(self::atLeast(3)) ->method('getCapabilities') - ->willReturn($this->createCapabilities($adapter)); + ->willReturn($this->createCapabilities()); $adapter ->expects(self::never()) @@ -973,11 +958,7 @@ public function testWillUsePcreMaximumQuantifierLengthIfAdapterAllowsMoreThanTha { $storage = $this->createMock(FlushableStorageAdapterInterface::class); $capabilities = $this->createCapabilities( - $storage, - null, - true, - 60, - SimpleCacheDecorator::$pcreMaximumQuantifierLength + maxKeyLength: SimpleCacheDecorator::$pcreMaximumQuantifierLength ); $storage @@ -1008,23 +989,22 @@ public function testPcreMaximumQuantifierLengthWontResultInCompilationError(): v ); } + /** + * @param SupportedDataTypesArrayShape|null $supportedDataTypes + * @param int<-1,max> $maxKeyLength + */ private function createCapabilities( - StorageInterface $storage, ?array $supportedDataTypes = null, - bool $staticTtl = true, - int $minTtl = 1, + bool $ttlSupported = true, int $maxKeyLength = -1, bool $useRequestTime = false, - bool $lockOnExpire = false ): Capabilities { - return new Capabilities($storage, new stdClass(), [ - 'supportedDatatypes' => $supportedDataTypes ?? $this->requiredTypes, - 'staticTtl' => $staticTtl, - 'minTtl' => $minTtl, - 'maxKeyLength' => $maxKeyLength, - 'useRequestTime' => $useRequestTime, - 'lockOnExpire' => $lockOnExpire, - ]); + return new Capabilities( + maxKeyLength: $maxKeyLength, + ttlSupported: $ttlSupported, + supportedDataTypes: $supportedDataTypes ?? self::REQUIRED_TYPES, + usesRequestTime: $useRequestTime, + ); } public function testKeepsDeferredItemsWhenCommitFails(): void diff --git a/test/Psr/SimpleCache/SimpleCacheDecoratorTest.php b/test/Psr/SimpleCache/SimpleCacheDecoratorTest.php index 05d831f8..c4c0698e 100644 --- a/test/Psr/SimpleCache/SimpleCacheDecoratorTest.php +++ b/test/Psr/SimpleCache/SimpleCacheDecoratorTest.php @@ -39,11 +39,11 @@ * try/catch blocks and assert identity against the result of getPrevious(). * * phpcs:disable Generic.Files.LineLength.TooLong + * @psalm-import-type SupportedDataTypesArrayShape from Capabilities */ -class SimpleCacheDecoratorTest extends TestCase +final class SimpleCacheDecoratorTest extends TestCase { - /** @var array */ - private array $requiredTypes = [ + private const SIMPLE_CACHE_REQUIRED_TYPES = [ 'NULL' => true, 'boolean' => true, 'integer' => true, @@ -68,7 +68,7 @@ class SimpleCacheDecoratorTest extends TestCase public function unsupportedCapabilities(): Generator { yield 'minimum key length <64 characters' => [ - $this->getMockCapabilities(null, true, 60, 63), + $this->createCapabilities(null, true, 63), ]; } @@ -80,43 +80,36 @@ protected function setUp(): void $this->cache = new SimpleCacheDecorator($this->storage); } - private function getMockCapabilities( + /** + * @param SupportedDataTypesArrayShape|null $supportedDataTypes + * @param int<-1,max> $maxKeyLength + */ + private function createCapabilities( ?array $supportedDataTypes = null, - bool $staticTtl = true, - int $minTtl = 60, + bool $ttlSupported = true, int $maxKeyLength = -1 ): Capabilities { - $supportedDataTypes = $supportedDataTypes ?? $this->requiredTypes; - $capabilities = $this->createMock(Capabilities::class); - $capabilities - ->method('getSupportedDatatypes') - ->willReturn($supportedDataTypes); - - $capabilities - ->method('getStaticTtl') - ->willReturn($staticTtl); - $capabilities - ->method('getMinTtl') - ->willReturn($minTtl); - - $capabilities - ->method('getMaxKeyLength') - ->willReturn($maxKeyLength); - - return $capabilities; + $supportedDataTypes = $supportedDataTypes ?? self::SIMPLE_CACHE_REQUIRED_TYPES; + return new Capabilities( + maxKeyLength: $maxKeyLength, + ttlSupported: $ttlSupported, + supportedDataTypes: $supportedDataTypes, + ); } + /** + * @param SupportedDataTypesArrayShape|null $supportedDataTypes + * @param int<-1,max> $maxKeyLength + */ private function mockCapabilities( - MockObject $storage, + MockObject&StorageInterface $storage, ?array $supportedDataTypes = null, - bool $staticTtl = true, - int $minTtl = 60, + bool $ttlSupported = true, int $maxKeyLength = -1 ): void { - $capabilities = $this->getMockCapabilities( + $capabilities = $this->createCapabilities( $supportedDataTypes, - $staticTtl, - $minTtl, + $ttlSupported, $maxKeyLength ); @@ -168,16 +161,14 @@ public function invalidatingTtls() public function testStorageNeedsSerializerWillThrowException(): void { - $dataTypes = [ - 'staticTtl' => true, - 'minTtl' => 1, - 'supportedDatatypes' => [ + $storage = $this->createMock(StorageInterface::class); + $this->mockCapabilities( + $storage, + supportedDataTypes: [ 'double' => false, ], - ]; + ); - $storage = $this->createMock(StorageInterface::class); - $this->mockCapabilities($storage, $dataTypes, false); $storage ->expects(self::never()) ->method('getOptions'); @@ -320,10 +311,10 @@ public function testSetShouldRemoveItemFromCacheIfTtlIsBelow1($ttl) self::assertTrue($this->cache->set('key', 'value', $ttl)); } - public function testSetShouldReturnFalseWhenProvidedWithPositiveTtlAndStorageDoesNotSupportPerItemTtl(): void + public function testSetShouldReturnFalseWhenProvidedWithPositiveTtlAndStorageDoesNotSupportTtl(): void { $storage = $this->createMock(StorageInterface::class); - $this->mockCapabilities($storage, null, false); + $this->mockCapabilities($storage, null, ttlSupported: false); $storage ->expects(self::never()) ->method('getOptions'); @@ -339,12 +330,11 @@ public function testSetShouldReturnFalseWhenProvidedWithPositiveTtlAndStorageDoe /** * @dataProvider invalidatingTtls - * @param int $ttl */ - public function testSetShouldRemoveItemFromCacheIfTtlIsBelow1AndStorageDoesNotSupportPerItemTtl($ttl) + public function testSetShouldRemoveItemFromCacheIfTtlIsBelow1AndStorageDoesNotSupportTtl(int $ttl): void { $storage = $this->createMock(StorageInterface::class); - $this->mockCapabilities($storage, null, false); + $this->mockCapabilities($storage, null, ttlSupported: false); $storage ->expects(self::never()) ->method('getOptions'); @@ -390,7 +380,7 @@ public function testSetShouldAcknowledgeStorageAdapterMaxKeyLengthWithPsrDecorat ->method('getOptions') ->willReturn($this->options); - $this->mockCapabilities($storage, null, false, 60, 251); + $this->mockCapabilities($storage, null, true, 251); $storage ->expects(self::once()) ->method('setItem') @@ -747,7 +737,7 @@ public function testSetMultipleShouldRemoveItemsFromCacheIfTtlIsBelow1($ttl) self::assertTrue($this->cache->setMultiple($values, $ttl)); } - public function testSetMultipleShouldReturnFalseWhenProvidedWithPositiveTtlAndStorageDoesNotSupportPerItemTtl(): void + public function testSetMultipleShouldReturnFalseWhenProvidedWithPositiveTtlAndStorageDoesNotSupportTtl(): void { $values = [ 'one' => 1, @@ -756,7 +746,7 @@ public function testSetMultipleShouldReturnFalseWhenProvidedWithPositiveTtlAndSt ]; $storage = $this->createMock(StorageInterface::class); - $this->mockCapabilities($storage, null, false); + $this->mockCapabilities($storage, null, ttlSupported: false); $storage ->expects(self::never()) ->method('getOptions'); @@ -772,9 +762,8 @@ public function testSetMultipleShouldReturnFalseWhenProvidedWithPositiveTtlAndSt /** * @dataProvider invalidatingTtls - * @param int $ttl */ - public function testSetMultipleShouldRemoveItemsFromCacheIfTtlIsBelow1AndStorageDoesNotSupportPerItemTtl($ttl) + public function testSetMultipleShouldRemoveItemsFromCacheIfTtlIsBelow1AndStorageDoesNotSupportTtl(int $ttl): void { $values = [ 'one' => 1, @@ -783,7 +772,7 @@ public function testSetMultipleShouldRemoveItemsFromCacheIfTtlIsBelow1AndStorage ]; $storage = $this->createMock(StorageInterface::class); - $this->mockCapabilities($storage, null, false); + $this->mockCapabilities($storage, ttlSupported: false); $storage ->expects(self::never()) ->method('getOptions'); @@ -986,7 +975,7 @@ public function testHasReRaisesExceptionThrownByStorage(): void public function testUseTtlFromOptionsWhenNotProvidedOnSet(): void { - $capabilities = $this->getMockCapabilities(); + $capabilities = $this->createCapabilities(); $storage = new TestAsset\TtlStorage(['ttl' => 20]); $storage->setCapabilities($capabilities); @@ -999,7 +988,7 @@ public function testUseTtlFromOptionsWhenNotProvidedOnSet(): void public function testUseTtlFromOptionsWhenNotProvidedOnSetMultiple(): void { - $capabilities = $this->getMockCapabilities(); + $capabilities = $this->createCapabilities(); $storage = new TestAsset\TtlStorage(['ttl' => 20]); $storage->setCapabilities($capabilities); @@ -1082,11 +1071,10 @@ public function testWillThrowExceptionWhenStorageDoesNotFulfillMinimumRequiremen public function testWillUsePcreMaximumQuantifierLengthIfAdapterAllowsMoreThanThat(): void { $storage = $this->createMock(StorageInterface::class); - $capabilities = $this->getMockCapabilities( + $capabilities = $this->createCapabilities( null, - true, - 60, - SimpleCacheDecorator::$pcreMaximumQuantifierLength + ttlSupported: true, + maxKeyLength: SimpleCacheDecorator::$pcreMaximumQuantifierLength ); $storage @@ -1121,7 +1109,7 @@ public function testGeneratorFloatKeyIsDetectedAsInvalidKey(): void { $storage = $this->createMock(StorageInterface::class); - $capabilities = $this->getMockCapabilities(); + $capabilities = $this->createCapabilities(); $storage ->method('getCapabilities') diff --git a/test/Storage/Adapter/AbstractAdapterTest.php b/test/Storage/Adapter/AbstractAdapterTest.php index beb911ca..81c87f62 100644 --- a/test/Storage/Adapter/AbstractAdapterTest.php +++ b/test/Storage/Adapter/AbstractAdapterTest.php @@ -24,7 +24,6 @@ use PHPUnit\Framework\TestCase; use ReflectionClass; use ReflectionMethod; -use stdClass; use function array_keys; use function array_merge; @@ -371,7 +370,7 @@ public function testGetItemReturnsNullIfFailed(): void public function simpleEventHandlingMethodDefinitions(): array { - $capabilities = new Capabilities($this->getMockForAbstractAdapter(), new stdClass()); + $capabilities = new Capabilities(); return [ // name, internalName, args, returnValue diff --git a/test/Storage/CapabilitiesTest.php b/test/Storage/CapabilitiesTest.php deleted file mode 100644 index f1bcec8f..00000000 --- a/test/Storage/CapabilitiesTest.php +++ /dev/null @@ -1,99 +0,0 @@ -marker = new stdClass(); - $this->adapter = $this->createMock(StorageInterface::class); - - $this->baseCapabilities = new Capabilities($this->adapter, $this->marker); - $this->capabilities = new Capabilities($this->adapter, $this->marker, [], $this->baseCapabilities); - } - - public function testGetAdapter(): void - { - self::assertSame($this->adapter, $this->capabilities->getAdapter()); - self::assertSame($this->adapter, $this->baseCapabilities->getAdapter()); - } - - public function testSetAndGetCapability(): void - { - $this->capabilities->setMaxTtl($this->marker, 100); - self::assertEquals(100, $this->capabilities->getMaxTtl()); - } - - public function testGetCapabilityByBaseCapabilities(): void - { - $this->baseCapabilities->setMaxTtl($this->marker, 100); - self::assertEquals(100, $this->capabilities->getMaxTtl()); - } - - public function testTriggerCapabilityEvent(): void - { - $eventManager = $this->createMock(EventManagerInterface::class); - - $adapter = $this->createMock(EventsCapableStorageInterface::class); - $adapter - ->expects(self::once()) - ->method('getEventManager') - ->willReturn($eventManager); - - $eventManager - ->expects(self::once()) - ->method('trigger') - ->with('capability', $adapter, self::callback(static function ($params): bool { - self::assertInstanceOf(ArrayObject::class, $params); - self::assertTrue(isset($params['maxTtl'])); - self::assertEquals(100, $params['maxTtl']); - return true; - })); - - $capabilities = new Capabilities($adapter, $this->marker); - $capabilities->setMaxTtl($this->marker, 100); - } -} diff --git a/test/Storage/TestAsset/EventsCapableStorageInterface.php b/test/Storage/TestAsset/EventsCapableStorageInterface.php deleted file mode 100644 index b2b7a459..00000000 --- a/test/Storage/TestAsset/EventsCapableStorageInterface.php +++ /dev/null @@ -1,12 +0,0 @@ -