From 680478bc4512f864f12e17ee06836b4da7ea3ce4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maximilian=20B=C3=B6sing?= <2189546+boesing@users.noreply.github.com> Date: Sun, 2 May 2021 21:08:44 +0200 Subject: [PATCH] qa: deprecate `storage` option for `PatternOptions` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since v2.10, the storage adapter have their own package. In v3.0 these, adapters have to register themselves to the `AdapterPluginManager` when the plugin manager is being instantiated and thus, this component wont be aware of which adapters are available without using a `PSR-11` container. This commit aims to provide a proper dependency injection layer to all cache patterns while deprecating the old `storage` option for `PatternOptions`. Signed-off-by: Maximilian Bösing <2189546+boesing@users.noreply.github.com> --- docs/book/pattern/callback-cache.md | 30 ++-- docs/book/pattern/capture-cache.md | 26 ++-- docs/book/pattern/class-cache.md | 53 ++++--- docs/book/pattern/intro.md | 40 +++--- docs/book/pattern/object-cache.md | 55 ++++--- docs/book/pattern/output-cache.md | 34 +++-- src/Pattern/AbstractPattern.php | 25 +--- src/Pattern/AbstractStorageCapablePattern.php | 70 +++++++++ src/Pattern/CallbackCache.php | 16 +-- src/Pattern/ClassCache.php | 13 +- src/Pattern/ObjectCache.php | 17 +-- src/Pattern/OutputCache.php | 18 +-- src/Pattern/PatternCacheFactory.php | 38 +++++ src/Pattern/PatternInterface.php | 1 + src/Pattern/PatternOptions.php | 5 + src/Pattern/StorageCapableInterface.php | 16 +++ src/Pattern/StoragePatternCacheFactory.php | 70 +++++++++ .../PatternPluginManagerTrait.php | 4 +- .../PatternPluginManagerV2Polyfill.php | 20 +-- .../PatternPluginManagerV3Polyfill.php | 20 +-- ...Test.php => AbstractCommonPatternTest.php} | 23 ++- .../AbstractCommonStoragePatternTest.php | 54 +++++++ ...Test.php => CallbackCacheTestAbstract.php} | 58 ++++---- ...eTest.php => CaptureCacheTestAbstract.php} | 79 +++++----- ...cheTest.php => ClassCacheTestAbstract.php} | 41 +++--- ...heTest.php => ObjectCacheTestAbstract.php} | 68 ++++----- ...heTest.php => OutputCacheTestAbstract.php} | 45 +++--- test/Pattern/PatternCacheFactoryTest.php | 73 ++++++++++ .../StoragePatternCacheFactoryTest.php | 135 ++++++++++++++++++ test/Pattern/TestAsset/TestCachePattern.php | 11 ++ .../{TestClassTest.php => TestClassCache.php} | 0 test/PatternPluginManagerTest.php | 40 ++++++ 32 files changed, 853 insertions(+), 345 deletions(-) create mode 100644 src/Pattern/AbstractStorageCapablePattern.php create mode 100644 src/Pattern/PatternCacheFactory.php create mode 100644 src/Pattern/StorageCapableInterface.php create mode 100644 src/Pattern/StoragePatternCacheFactory.php rename test/Pattern/{CommonPatternTest.php => AbstractCommonPatternTest.php} (74%) create mode 100644 test/Pattern/AbstractCommonStoragePatternTest.php rename test/Pattern/{CallbackCacheTest.php => CallbackCacheTestAbstract.php} (67%) rename test/Pattern/{CaptureCacheTest.php => CaptureCacheTestAbstract.php} (65%) rename test/Pattern/{ClassCacheTest.php => ClassCacheTestAbstract.php} (64%) rename test/Pattern/{ObjectCacheTest.php => ObjectCacheTestAbstract.php} (56%) rename test/Pattern/{OutputCacheTest.php => OutputCacheTestAbstract.php} (58%) create mode 100644 test/Pattern/PatternCacheFactoryTest.php create mode 100644 test/Pattern/StoragePatternCacheFactoryTest.php create mode 100644 test/Pattern/TestAsset/TestCachePattern.php rename test/Pattern/TestAsset/{TestClassTest.php => TestClassCache.php} (100%) diff --git a/docs/book/pattern/callback-cache.md b/docs/book/pattern/callback-cache.md index 9a141e26..4a1fee46 100644 --- a/docs/book/pattern/callback-cache.md +++ b/docs/book/pattern/callback-cache.md @@ -5,42 +5,40 @@ The callback cache pattern caches the results of arbitrary PHP callables. ## Quick Start ```php -use Laminas\Cache\PatternFactory; +use Laminas\Cache\Pattern\CallbackCache; use Laminas\Cache\Pattern\PatternOptions; +use Laminas\Cache\Storage\StorageInterface; -// Via the factory: -$callbackCache = PatternFactory::factory('callback', [ - 'storage' => 'apc', - 'cache_output' => true, -]); +/** @var StorageInterface $storage */ +$storage = null; // Can be any instance of StorageInterface // Or the equivalent manual instantiation: -$callbackCache = new \Laminas\Cache\Pattern\CallbackCache(); -$callbackCache->setOptions(new PatternOptions([ - 'storage' => 'apc', - 'cache_output' => true, -])); +$callbackCache = new CallbackCache( + $storage, + new PatternOptions([ + 'cache_output' => true, + ]) +); ``` ## Configuration Options Option | Data Type | Default Value | Description ------ | --------- | ------------- | ----------- -`storage` | `string | array | Laminas\Cache\Storage\StorageInterface` | none | Adapter used for reading and writing cached data. -`cache_output` | `boolean` | `true` | Whether or not to cache callback output. +`storage` | `string | array | Laminas\Cache\Storage\StorageInterface` | none | **deprecated** Adapter used for reading and writing cached data. +`cache_output` | `bool` | `true` | Whether or not to cache callback output. ## Available Methods -In addition to the methods defined in the `PatternInterface`, this +In addition to the methods defined in the `PatternInterface` and the `StorageCapableInterface`, this implementation provides the following methods. ```php namespace Laminas\Cache\Pattern; use Laminas\Cache\Exception; -use Laminas\Stdlib\ErrorHandler; -class CallbackCache extends AbstractPattern +class CallbackCache extends AbstractStorageCapablePattern { /** * Call the specified callback or get the result from cache diff --git a/docs/book/pattern/capture-cache.md b/docs/book/pattern/capture-cache.md index d8910788..fb1dff91 100644 --- a/docs/book/pattern/capture-cache.md +++ b/docs/book/pattern/capture-cache.md @@ -20,10 +20,14 @@ ErrorDocument 404 /index.php And add the cache to the related application script, e.g. `index.php`: ```php -use Laminas\Cache\PatternFactory; -$capture = Laminas\Cache\PatternFactory::factory('capture', [ - 'public_dir' => __DIR__, -]); +use Laminas\Cache\Pattern\CaptureCache; +use Laminas\Cache\Pattern\PatternOptions; + +$capture = new CaptureCache( + new PatternOptions([ + 'public_dir' => __DIR__, + ]) +); // Start capturing all output, excluding headers, and write to the public // directory: @@ -44,7 +48,7 @@ Option | Data Type | Default Value | Description `file_locking` | `bool` | `true` | Whether or not to lock output files when writing. `file_permission` | `int | bool` | `0600` (`false` on Windows) | Default permissions for generated output files. `dir_permission` | `int | bool` | `0700` (`false` on Windows) | Default permissions for generated output directories. -`umask` | `int` | `bool` | `false` | Whether or not to umask generated output files / directories. +`umask` | `int | false` | `false` | Whether or not to umask generated output files / directories. ## Available Methods @@ -55,7 +59,6 @@ exposes the following methods. namespace Laminas\Cache\Pattern; use Laminas\Cache\Exception; -use Laminas\Stdlib\ErrorHandler; class CaptureCache extends AbstractPattern { @@ -138,9 +141,14 @@ Use the following script: ```php // index.php -$captureCache = Laminas\Cache\PatternFactory::factory('capture', [ - 'public_dir' => __DIR__, -]); +use Laminas\Cache\Pattern\CaptureCache; +use Laminas\Cache\Pattern\PatternOptions; + +$capture = new CaptureCache( + new PatternOptions([ + 'public_dir' => __DIR__, + ]) +); // TODO ``` diff --git a/docs/book/pattern/class-cache.md b/docs/book/pattern/class-cache.md index 8031a989..c3eb20c1 100644 --- a/docs/book/pattern/class-cache.md +++ b/docs/book/pattern/class-cache.md @@ -8,34 +8,40 @@ class being cached, and caches static properties. ## Quick Start ```php -use Laminas\Cache\PatternFactory; - -$classCache = PatternFactory::factory('class', [ - 'class' => 'MyClass', - 'storage' => 'apc', -]); +use Laminas\Cache\Pattern\ClassCache; +use Laminas\Cache\Pattern\PatternOptions; +use Laminas\Cache\Storage\StorageInterface; + +/** @var StorageInterface $storage */ +$storage = null; // Can be any instance of StorageInterface + +$classCache = new ClassCache( + $storage, + new PatternOptions([ + 'class' => 'MyClass', + ]) +); ``` ## Configuration Options Option | Data Type | Default Value | Description ------ | --------- | ------------- | ----------- -`storage` | `string | array | Laminas\Cache\Storage\StorageInterface` | none | Adapter used for reading and writing cached data. +`storage` | `string | array | Laminas\Cache\Storage\StorageInterface` | none | **deprecated** Adapter used for reading and writing cached data. `class` | `string` | none | Name of the class for which to cache method output. -`cache_output` | `boolean` | `true` | Whether or not to cache method output. -`cache_by_default` | `boolean` | `true` | Cache all method calls by default. +`cache_output` | `bool` | `true` | Whether or not to cache method output. +`cache_by_default` | `bool` | `true` | Cache all method calls by default. `class_cache_methods` | `array` | `[]` | List of methods to cache (if `cache_by_default` is disabled). `class_non_cache_methods` | `array` | `[]` | List of methods to omit from caching (if `cache_by_default` is enabled). ## Available Methods -In addition to the methods defined in `PatternInterface`, this implementation +In addition to the methods defined in `PatternInterface` and the `StorageCapableInterface`, this implementation exposes the following methods. ```php namespace Laminas\Cache\Pattern; -use Laminas\Cache; use Laminas\Cache\Exception; class ClassCache extends CallbackCache @@ -134,14 +140,23 @@ class ClassCache extends CallbackCache ### Caching of Import Feeds ```php -$cachedFeedReader = Laminas\Cache\PatternFactory::factory('class', [ - 'class' => 'Laminas\Feed\Reader\Reader', - 'storage' => 'apc', - - // The feed reader doesn't output anything, - // so the output doesn't need to be caught and cached: - 'cache_output' => false, -]); +use Laminas\Cache\Pattern\ClassCache; +use Laminas\Cache\Pattern\PatternOptions; +use Laminas\Cache\Storage\StorageInterface; + +/** @var StorageInterface $storage */ +$storage = null; // Can be any instance of StorageInterface + +$cachedFeedReader = new ClassCache( + $storage, + new PatternOptions([ + 'class' => \Laminas\Feed\Reader\Reader::class, + + // The feed reader doesn't output anything, + // so the output doesn't need to be caught and cached: + 'cache_output' => false, + ]) +); $feed = $cachedFeedReader->call("import", array('http://www.planet-php.net/rdf/')); diff --git a/docs/book/pattern/intro.md b/docs/book/pattern/intro.md index f0a1cac7..a968828f 100644 --- a/docs/book/pattern/intro.md +++ b/docs/book/pattern/intro.md @@ -26,21 +26,23 @@ Pattern objects can either be created from the provided `Laminas\Cache\PatternFa by instantiating one of the `Laminas\Cache\Pattern\*Cache` classes. ```php -// Via the factory: -$callbackCache = Laminas\Cache\PatternFactory::factory('callback', [ - 'storage' => 'apc', -]); +use Laminas\Cache\Pattern\CallbackCache; +use Laminas\Cache\Pattern\PatternOptions; +use Laminas\Cache\Storage\StorageInterface; + +/** @var StorageInterface $storage */ +$storage = null; // Can be any instance of StorageInterface // Or the equivalent manual instantiation: -$callbackCache = new Laminas\Cache\Pattern\CallbackCache(); -$callbackCache->setOptions(new Laminas\Cache\Pattern\PatternOptions([ - 'storage' => 'apc', -])); +$callbackCache = new CallbackCache( + $storage, + new PatternOptions() +); ``` ## Available Methods -The following methods are implemented by `Laminas\Cache\Pattern\AbstractPattern`. +The following methods are implemented by every cache pattern. Please read documentation of specific patterns to get more information. ```php @@ -48,13 +50,6 @@ namespace Laminas\Cache\Pattern; interface PatternInterface { - /** - * Set pattern options - * - * @param PatternOptions $options - * @return PatternInterface - */ - public function setOptions(PatternOptions $options); /** * Get all pattern options @@ -64,3 +59,16 @@ interface PatternInterface public function getOptions(); } ``` + +There are cache patterns which depend on a storage. In this case, these adapters implement the `StorageCapableInterface`: + +```php +namespace Laminas\Cache\Pattern; + +use Laminas\Cache\Storage\StorageInterface; + +interface StorageCapableInterface extends PatternInterface +{ + public function getStorage(): ?StorageInterface; +} +``` diff --git a/docs/book/pattern/object-cache.md b/docs/book/pattern/object-cache.md index 703c5775..f905df60 100644 --- a/docs/book/pattern/object-cache.md +++ b/docs/book/pattern/object-cache.md @@ -7,32 +7,39 @@ public properties. ## Quick Start ```php +use Laminas\Cache\Pattern\ObjectCache; +use Laminas\Cache\Pattern\PatternOptions; +use Laminas\Cache\Storage\StorageInterface; use stdClass; -use Laminas\Cache\PatternFactory; + +/** @var StorageInterface $storage */ +$storage = null; // Can be any instance of StorageInterface $object = new stdClass(); -$objectCache = PatternFactory::factory('object', [ - 'object' => $object, - 'storage' => 'apc' -]); +$objectCache = new ObjectCache( + $storage, + new PatternOptions([ + 'object' => $object, + ]) +); ``` ## Configuration Options Option | Data Type | Default Value | Description ------ | --------- | ------------- | ----------- -`storage` | `string | array | Laminas\Cache\Storage\StorageInterface` | none | Adapter used for reading and writing cached data. +`storage` | `string | array | Laminas\Cache\Storage\StorageInterface` | none | **deprecated** Adapter used for reading and writing cached data. `object` | `object` | none | The object for which to cache method calls. `object_key` | `null | string` | Class name of object | Hopefully unique! -`cache_output` | `boolean` | `true` | Whether or not to cache method output. -`cache_by_default` | `boolean` | `true` | Cache all method calls by default. +`cache_output` | `bool` | `true` | Whether or not to cache method output. +`cache_by_default` | `bool` | `true` | Cache all method calls by default. `object_cache_methods` | `array` | `[]` | List of methods to cache (if `cache_by_default` is disabled). `object_non_cache_methods` | `array` | `[]` | List of methods to blacklist (if `cache_by_default` is enabled). -`object_cache_magic_properties` | `boolean` | `false` | Whether or not to cache properties exposed by method overloading. +`object_cache_magic_properties` | `bool` | `false` | Whether or not to cache properties exposed by method overloading. ## Available Methods -In addition to the methods defined in `PatternInterface`, this implementation +In addition to the methods defined in `PatternInterface` and the `StorageCapableInterface`, this implementation defines the following methods. ```php @@ -153,16 +160,24 @@ class ObjectCache extends CallbackCache ### Caching a Filter ```php -$filter = new Laminas\Filter\RealPath(); -$cachedFilter = Laminas\Cache\PatternFactory::factory('object', [ - 'object' => $filter, - 'object_key' => 'RealpathFilter', - 'storage' => 'apc', - - // The realpath filter doesn't output anything - // so the output don't need to be caught and cached - 'cache_output' => false, -]); +use Laminas\Cache\Pattern\ObjectCache; +use Laminas\Cache\Pattern\PatternOptions; +use Laminas\Cache\Storage\StorageInterface; + +/** @var StorageInterface $storage */ +$storage = null; // Can be any instance of StorageInterface +$filter = new \Laminas\Filter\RealPath(); +$cachedFilter = new ObjectCache( + $storage, + new PatternOptions([ + 'object' => $filter, + 'object_key' => 'RealpathFilter', + + // The realpath filter doesn't output anything + // so the output don't need to be caught and cached + 'cache_output' => false, + ]) +); $path = $cachedFilter->call("filter", ['/www/var/path/../../mypath']); diff --git a/docs/book/pattern/output-cache.md b/docs/book/pattern/output-cache.md index eff9d3c6..4e56adf7 100644 --- a/docs/book/pattern/output-cache.md +++ b/docs/book/pattern/output-cache.md @@ -5,22 +5,28 @@ The `OutputCache` pattern caches output between calls to `start()` and `end()`. ## Quick Start ```php -use Laminas\Cache\PatternFactory; +use Laminas\Cache\Pattern\OutputCache; +use Laminas\Cache\Pattern\PatternOptions; +use Laminas\Cache\Storage\StorageInterface; -$outputCache = PatternFactory::factory('output', [ - 'storage' => 'apc' -]); +/** @var StorageInterface $storage */ +$storage = null; // Can be any instance of StorageInterface + +$outputCache = new OutputCache( + $storage, + new PatternOptions() +); ``` ## Configuration Options Option | Data Type | Default Value | Description ------ | --------- | ------------- | ----------- -`storage` | `string | array | Laminas\Cache\Storage\StorageInterface` | none | Adapter used for reading and writing cached data. +`storage` | `string | array | Laminas\Cache\Storage\StorageInterface` | none | **deprecated** Adapter used for reading and writing cached data. ## Available Methods -In addition to the methods defined in `PatternInterface`, this implementation +In addition to the methods defined in `PatternInterface` and `StorageCapableInterface`, this implementation defines the following methods. ```php @@ -28,7 +34,7 @@ namespace Laminas\Cache\Pattern; use Laminas\Cache\Exception; -class OutputCache extends AbstractPattern +class OutputCache extends AbstractStorageCapablePattern { /** * If there is a cached item with the given key, display its data, and @@ -57,9 +63,17 @@ class OutputCache extends AbstractPattern ### Caching simple View Scripts ```php -$outputCache = Laminas\Cache\PatternFactory::factory('output', [ - 'storage' => 'apc', -]); +use Laminas\Cache\Pattern\OutputCache; +use Laminas\Cache\Pattern\PatternOptions; +use Laminas\Cache\Storage\StorageInterface; + +/** @var StorageInterface $storage */ +$storage = null; // Can be any instance of StorageInterface + +$outputCache = new OutputCache( + $storage, + new PatternOptions() +); $outputCache->start('mySimpleViewScript'); include '/path/to/view/script.phtml'; diff --git a/src/Pattern/AbstractPattern.php b/src/Pattern/AbstractPattern.php index be8a5091..efc57599 100644 --- a/src/Pattern/AbstractPattern.php +++ b/src/Pattern/AbstractPattern.php @@ -8,37 +8,24 @@ namespace Laminas\Cache\Pattern; -use Laminas\Cache\Exception; - abstract class AbstractPattern implements PatternInterface { /** - * @var PatternOptions + * @var PatternOptions|null */ protected $options; - /** - * Set pattern options - * - * @param PatternOptions $options - * @return AbstractPattern Provides a fluent interface - * @throws Exception\InvalidArgumentException - */ - public function setOptions(PatternOptions $options) + public function __construct(?PatternOptions $options = null) { - if (! $options instanceof PatternOptions) { - $options = new PatternOptions($options); - } + $this->options = $options; + } + public function setOptions(PatternOptions $options) + { $this->options = $options; return $this; } - /** - * Get all pattern options - * - * @return PatternOptions - */ public function getOptions() { if (null === $this->options) { diff --git a/src/Pattern/AbstractStorageCapablePattern.php b/src/Pattern/AbstractStorageCapablePattern.php new file mode 100644 index 00000000..55e8113f --- /dev/null +++ b/src/Pattern/AbstractStorageCapablePattern.php @@ -0,0 +1,70 @@ +storage = $storage; + $this->assertStorageMatchesStorageFromOptions($storage, $options); + } + + private function assertStorageMatchesStorageFromOptions(?StorageInterface $storage, ?PatternOptions $options): void + { + if ($storage === null) { + return; + } + + if ($options === null) { + return; + } + + $storageViaOptions = $options->getStorage(); + if ($storageViaOptions === null) { + return; + } + + if ($storageViaOptions === $storage) { + return; + } + + throw new InvalidArgumentException(sprintf( + 'Storage passed to %s is not the same as passed to the %s.', + self::class, + PatternOptions::class + )); + } + + public function setOptions(PatternOptions $options) + { + $this->assertStorageMatchesStorageFromOptions($this->storage, $options); + return parent::setOptions($options); + } + + public function getStorage(): ?StorageInterface + { + $options = $this->getOptions(); + $storage = $options->getStorage(); + if ($storage !== null) { + return $storage; + } + + return $this->storage; + } +} diff --git a/src/Pattern/CallbackCache.php b/src/Pattern/CallbackCache.php index c450de1e..09238735 100644 --- a/src/Pattern/CallbackCache.php +++ b/src/Pattern/CallbackCache.php @@ -9,24 +9,20 @@ namespace Laminas\Cache\Pattern; use Laminas\Cache\Exception; +use Laminas\Cache\Storage\StorageInterface; use Laminas\Stdlib\ErrorHandler; -class CallbackCache extends AbstractPattern +class CallbackCache extends AbstractStorageCapablePattern { - /** - * Set options - * - * @param PatternOptions $options - * @return CallbackCache Provides a fluent interface - * @throws Exception\InvalidArgumentException if missing storage option - */ public function setOptions(PatternOptions $options) { parent::setOptions($options); + $storage = $this->getStorage(); - if (! $options->getStorage()) { + if (! $storage instanceof StorageInterface) { throw new Exception\InvalidArgumentException("Missing option 'storage'"); } + return $this; } @@ -42,7 +38,7 @@ public function setOptions(PatternOptions $options) public function call($callback, array $args = []) { $options = $this->getOptions(); - $storage = $options->getStorage(); + $storage = $this->getStorage(); $success = null; $key = $this->generateCallbackKey($callback, $args); $result = $storage->getItem($key, $success); diff --git a/src/Pattern/ClassCache.php b/src/Pattern/ClassCache.php index 2206fd84..88b56813 100644 --- a/src/Pattern/ClassCache.php +++ b/src/Pattern/ClassCache.php @@ -13,22 +13,19 @@ class ClassCache extends CallbackCache { - /** - * Set options - * - * @param PatternOptions $options - * @return ClassCache Provides a fluent interface - * @throws Exception\InvalidArgumentException if missing 'class' or 'storage' options - */ + public function setOptions(PatternOptions $options) { parent::setOptions($options); if (! $options->getClass()) { throw new Exception\InvalidArgumentException("Missing option 'class'"); - } elseif (! $options->getStorage()) { + } + + if (! $this->getStorage()) { throw new Exception\InvalidArgumentException("Missing option 'storage'"); } + return $this; } diff --git a/src/Pattern/ObjectCache.php b/src/Pattern/ObjectCache.php index e0748476..5267701b 100644 --- a/src/Pattern/ObjectCache.php +++ b/src/Pattern/ObjectCache.php @@ -12,22 +12,17 @@ class ObjectCache extends CallbackCache { - /** - * Set options - * - * @param PatternOptions $options - * @return void - * @throws Exception\InvalidArgumentException - */ public function setOptions(PatternOptions $options) { parent::setOptions($options); if (! $options->getObject()) { throw new Exception\InvalidArgumentException("Missing option 'object'"); - } elseif (! $options->getStorage()) { + } elseif (! $this->getStorage()) { throw new Exception\InvalidArgumentException("Missing option 'storage'"); } + + return $this; } /** @@ -70,7 +65,8 @@ public function call($method, array $args = []) $removeKeys[] = $this->generateKey('__isset', [$property]); } if ($removeKeys) { - $options->getStorage()->removeItems($removeKeys); + $storage = $this->getStorage(); + $storage->removeItems($removeKeys); } return; @@ -123,7 +119,8 @@ public function call($method, array $args = []) $removeKeys[] = $this->generateKey('__isset', [$property]); } if ($removeKeys) { - $options->getStorage()->removeItems($removeKeys); + $storage = $this->getStorage(); + $storage->removeItems($removeKeys); } return; } diff --git a/src/Pattern/OutputCache.php b/src/Pattern/OutputCache.php index 5a946b55..da2021a0 100644 --- a/src/Pattern/OutputCache.php +++ b/src/Pattern/OutputCache.php @@ -10,7 +10,7 @@ use Laminas\Cache\Exception; -class OutputCache extends AbstractPattern +class OutputCache extends AbstractStorageCapablePattern { /** * The key stack @@ -19,18 +19,12 @@ class OutputCache extends AbstractPattern */ protected $keyStack = []; - /** - * Set options - * - * @param PatternOptions $options - * @return OutputCache Provides a fluent interface - * @throws Exception\InvalidArgumentException - */ + public function setOptions(PatternOptions $options) { parent::setOptions($options); - if (! $options->getStorage()) { + if (! $this->getStorage()) { throw new Exception\InvalidArgumentException("Missing option 'storage'"); } @@ -52,7 +46,8 @@ public function start($key) } $success = null; - $data = $this->getOptions()->getStorage()->getItem($key, $success); + $storage = $this->getStorage(); + $data = $storage->getItem($key, $success); if ($success) { echo $data; return true; @@ -83,6 +78,7 @@ public function end() throw new Exception\RuntimeException('Output buffering not active'); } - return $this->getOptions()->getStorage()->setItem($key, $output); + $storage = $this->getStorage(); + return $storage->setItem($key, $output); } } diff --git a/src/Pattern/PatternCacheFactory.php b/src/Pattern/PatternCacheFactory.php new file mode 100644 index 00000000..c81b1929 --- /dev/null +++ b/src/Pattern/PatternCacheFactory.php @@ -0,0 +1,38 @@ + $requestedName + */ + public function __invoke( + ContainerInterface $container, + string $requestedName, + array $options = null + ): PatternInterface { + if (! class_exists($requestedName)) { + throw new InvalidArgumentException(sprintf( + 'Factory %s is used for a service %s which does not exist.' + . ' Please only use this factory for services with full qualified class names!', + self::class, + $requestedName + )); + } + + $patternOptions = new PatternOptions($options); + return new $requestedName($patternOptions); + } +} diff --git a/src/Pattern/PatternInterface.php b/src/Pattern/PatternInterface.php index 3aebd63e..41b00b75 100644 --- a/src/Pattern/PatternInterface.php +++ b/src/Pattern/PatternInterface.php @@ -15,6 +15,7 @@ interface PatternInterface * * @param PatternOptions $options * @return PatternInterface + * @deprecated This method will be removed with v3.0. Options should be passed via instantiation. */ public function setOptions(PatternOptions $options); diff --git a/src/Pattern/PatternOptions.php b/src/Pattern/PatternOptions.php index 55875013..16900121 100644 --- a/src/Pattern/PatternOptions.php +++ b/src/Pattern/PatternOptions.php @@ -680,7 +680,10 @@ public function getPublicDir() * - OutputCache * * @param string|array|Storage $storage + * * @return PatternOptions Provides a fluent interface + * @deprecated This method will be removed with v3.0.0. + * The underlying storage will be a dependency of the cache pattern instead. */ public function setStorage($storage) { @@ -698,6 +701,8 @@ public function setStorage($storage) * - OutputCache * * @return null|Storage + * @deprecated This method will be removed with v3.0.0. + * The underlying storage will be a dependency of the cache pattern instead. */ public function getStorage() { diff --git a/src/Pattern/StorageCapableInterface.php b/src/Pattern/StorageCapableInterface.php new file mode 100644 index 00000000..5a51d685 --- /dev/null +++ b/src/Pattern/StorageCapableInterface.php @@ -0,0 +1,16 @@ +storageFactory = $storageFactory ?? static function ($container, $storageOption): StorageInterface { + if (is_string($storageOption)) { + $adapterPluginManager = $container->get(AdapterPluginManager::class); + return $adapterPluginManager->get($storageOption); + } + + return StorageFactory::factory($storageOption); + }; + } + + /** + * @template T of StorageCapableInterface + * @psalm-param class-string $requestedName + * @psalm-return T + */ + public function __invoke( + ContainerInterface $container, + string $requestedName, + array $options = null + ): StorageCapableInterface { + if (! class_exists($requestedName)) { + throw new InvalidArgumentException(sprintf( + 'Factory %s is used for a service %s which does not exist.' + . ' Please only use this factory for services with full qualified class names!', + self::class, + $requestedName + )); + } + + $storageOption = $options['storage'] ?? null; + $storageInstance = null; + if (is_array($storageOption) || is_string($storageOption)) { + $storageInstance = ($this->storageFactory)($container, $storageOption); + $options['storage'] = $storageInstance; + } + + $patternOptions = new PatternOptions($options); + return new $requestedName($storageInstance, $patternOptions); + } +} diff --git a/src/PatternPluginManager/PatternPluginManagerTrait.php b/src/PatternPluginManager/PatternPluginManagerTrait.php index 2ac2243a..49835e91 100644 --- a/src/PatternPluginManager/PatternPluginManagerTrait.php +++ b/src/PatternPluginManager/PatternPluginManagerTrait.php @@ -28,7 +28,9 @@ trait PatternPluginManagerTrait */ public function build($plugin, array $options = null) { - if (empty($options)) { + $options = $options ?? []; + + if ($options === []) { return parent::build($plugin); } diff --git a/src/PatternPluginManager/PatternPluginManagerV2Polyfill.php b/src/PatternPluginManager/PatternPluginManagerV2Polyfill.php index 3c2f9bb2..f62d0fde 100644 --- a/src/PatternPluginManager/PatternPluginManagerV2Polyfill.php +++ b/src/PatternPluginManager/PatternPluginManagerV2Polyfill.php @@ -51,18 +51,18 @@ class PatternPluginManagerV2Polyfill extends AbstractPluginManager ]; protected $factories = [ - Pattern\CallbackCache::class => InvokableFactory::class, - Pattern\CaptureCache::class => InvokableFactory::class, - Pattern\ClassCache::class => InvokableFactory::class, - Pattern\ObjectCache::class => InvokableFactory::class, - Pattern\OutputCache::class => InvokableFactory::class, + Pattern\CallbackCache::class => Pattern\StoragePatternCacheFactory::class, + Pattern\CaptureCache::class => Pattern\PatternCacheFactory::class, + Pattern\ClassCache::class => Pattern\StoragePatternCacheFactory::class, + Pattern\ObjectCache::class => Pattern\StoragePatternCacheFactory::class, + Pattern\OutputCache::class => Pattern\StoragePatternCacheFactory::class, // v2 normalized FQCNs - 'laminascachepatterncallbackcache' => InvokableFactory::class, - 'laminascachepatterncapturecache' => InvokableFactory::class, - 'laminascachepatternclasscache' => InvokableFactory::class, - 'laminascachepatternobjectcache' => InvokableFactory::class, - 'laminascachepatternoutputcache' => InvokableFactory::class, + 'laminascachepatterncallbackcache' => Pattern\StoragePatternCacheFactory::class, + 'laminascachepatterncapturecache' => Pattern\PatternCacheFactory::class, + 'laminascachepatternclasscache' => Pattern\StoragePatternCacheFactory::class, + 'laminascachepatternobjectcache' => Pattern\StoragePatternCacheFactory::class, + 'laminascachepatternoutputcache' => Pattern\StoragePatternCacheFactory::class, ]; /** diff --git a/src/PatternPluginManager/PatternPluginManagerV3Polyfill.php b/src/PatternPluginManager/PatternPluginManagerV3Polyfill.php index 09957642..b1706b2a 100644 --- a/src/PatternPluginManager/PatternPluginManagerV3Polyfill.php +++ b/src/PatternPluginManager/PatternPluginManagerV3Polyfill.php @@ -51,18 +51,18 @@ class PatternPluginManagerV3Polyfill extends AbstractPluginManager ]; protected $factories = [ - Pattern\CallbackCache::class => InvokableFactory::class, - Pattern\CaptureCache::class => InvokableFactory::class, - Pattern\ClassCache::class => InvokableFactory::class, - Pattern\ObjectCache::class => InvokableFactory::class, - Pattern\OutputCache::class => InvokableFactory::class, + Pattern\CallbackCache::class => Pattern\StoragePatternCacheFactory::class, + Pattern\CaptureCache::class => Pattern\PatternCacheFactory::class, + Pattern\ClassCache::class => Pattern\StoragePatternCacheFactory::class, + Pattern\ObjectCache::class => Pattern\StoragePatternCacheFactory::class, + Pattern\OutputCache::class => Pattern\StoragePatternCacheFactory::class, // v2 normalized FQCNs - 'laminascachepatterncallbackcache' => InvokableFactory::class, - 'laminascachepatterncapturecache' => InvokableFactory::class, - 'laminascachepatternclasscache' => InvokableFactory::class, - 'laminascachepatternobjectcache' => InvokableFactory::class, - 'laminascachepatternoutputcache' => InvokableFactory::class, + 'laminascachepatterncallbackcache' => Pattern\StoragePatternCacheFactory::class, + 'laminascachepatterncapturecache' => Pattern\PatternCacheFactory::class, + 'laminascachepatternclasscache' => Pattern\StoragePatternCacheFactory::class, + 'laminascachepatternobjectcache' => Pattern\StoragePatternCacheFactory::class, + 'laminascachepatternoutputcache' => Pattern\StoragePatternCacheFactory::class, ]; /** diff --git a/test/Pattern/CommonPatternTest.php b/test/Pattern/AbstractCommonPatternTest.php similarity index 74% rename from test/Pattern/CommonPatternTest.php rename to test/Pattern/AbstractCommonPatternTest.php index d59d61af..a04bf1d5 100644 --- a/test/Pattern/CommonPatternTest.php +++ b/test/Pattern/AbstractCommonPatternTest.php @@ -11,32 +11,31 @@ use Laminas\Cache\PatternPluginManager; use Laminas\ServiceManager\ServiceManager; use PHPUnit\Framework\TestCase; +use Laminas\Cache\Pattern\PatternInterface; /** * @group Laminas_Cache * @covers Laminas\Cache\Pattern\PatternOptions */ -abstract class CommonPatternTest extends TestCase +abstract class AbstractCommonPatternTest extends TestCase { - // @codingStandardsIgnoreStart /** * @var \Laminas\Cache\Pattern\PatternInterface */ - protected $_pattern; - // @codingStandardsIgnoreEnd + protected $pattern; protected function setUp(): void { - $this->assertInstanceOf( - 'Laminas\Cache\Pattern\PatternInterface', - $this->_pattern, + self::assertInstanceOf( + PatternInterface::class, + $this->pattern, 'Internal pattern instance is needed for tests' ); } public function tearDown(): void { - unset($this->_pattern); + unset($this->pattern); } /** @@ -58,14 +57,14 @@ public function testPatternPluginManagerWithCommonNames($commonPatternName) public function testOptionNamesValid() { - $options = $this->_pattern->getOptions(); + $options = $this->pattern->getOptions(); $this->assertInstanceOf('Laminas\Cache\Pattern\PatternOptions', $options); } public function testOptionsGetAndSetDefault() { - $options = $this->_pattern->getOptions(); - $this->_pattern->setOptions($options); - $this->assertSame($options, $this->_pattern->getOptions()); + $options = $this->pattern->getOptions(); + $this->pattern->setOptions($options); + $this->assertSame($options, $this->pattern->getOptions()); } } diff --git a/test/Pattern/AbstractCommonStoragePatternTest.php b/test/Pattern/AbstractCommonStoragePatternTest.php new file mode 100644 index 00000000..fdd702d8 --- /dev/null +++ b/test/Pattern/AbstractCommonStoragePatternTest.php @@ -0,0 +1,54 @@ +storage, + sprintf( + 'Internal pattern instance with implemented `%s` is needed for tests', + StorageCapableInterface::class + ) + ); + self::assertInstanceOf( + PatternOptions::class, + $this->options, + 'Pattern options are missing' + ); + parent::setUp(); + } + + public function testGetStorageReturnsStorage(): void + { + self::assertSame($this->storage, $this->pattern->getStorage()); + } +} diff --git a/test/Pattern/CallbackCacheTest.php b/test/Pattern/CallbackCacheTestAbstract.php similarity index 67% rename from test/Pattern/CallbackCacheTest.php rename to test/Pattern/CallbackCacheTestAbstract.php index 3be964d5..e079c424 100644 --- a/test/Pattern/CallbackCacheTest.php +++ b/test/Pattern/CallbackCacheTestAbstract.php @@ -11,6 +11,7 @@ use Laminas\Cache; use LaminasTest\Cache\Pattern\TestAsset\FailableCallback; use LaminasTest\Cache\Pattern\TestAsset\TestCallbackCache; +use Laminas\Cache\Exception\InvalidArgumentException; /** * Test function @@ -24,25 +25,18 @@ function bar() /** * @group Laminas_Cache */ -class CallbackCacheTest extends CommonPatternTest +class CallbackCacheTestAbstract extends AbstractCommonStoragePatternTest { - // @codingStandardsIgnoreStart - /** - * @var \Laminas\Cache\Storage\StorageInterface - */ - protected $_storage; - // @codingStandardsIgnoreEnd - protected function setUp(): void { - $this->_storage = new Cache\Storage\Adapter\Memory([ + $this->storage = new Cache\Storage\Adapter\Memory([ 'memory_limit' => 0 ]); - $this->_options = new Cache\Pattern\PatternOptions([ - 'storage' => $this->_storage, + $this->options = new Cache\Pattern\PatternOptions([ + 'storage' => $this->storage, ]); - $this->_pattern = new Cache\Pattern\CallbackCache(); - $this->_pattern->setOptions($this->_options); + $this->pattern = new Cache\Pattern\CallbackCache(); + $this->pattern->setOptions($this->options); parent::setUp(); } @@ -62,7 +56,7 @@ public function getCommonPatternNamesProvider() public function testCallEnabledCacheOutputByDefault() { - $this->_testCall( + $this->doTestCall( __NAMESPACE__ . '\TestAsset\TestCallbackCache::bar', ['testCallEnabledCacheOutputByDefault', 'arg2'] ); @@ -70,9 +64,9 @@ public function testCallEnabledCacheOutputByDefault() public function testCallDisabledCacheOutput() { - $options = $this->_pattern->getOptions(); + $options = $this->pattern->getOptions(); $options->setCacheOutput(false); - $this->_testCall( + $this->doTestCall( __NAMESPACE__ . '\TestAsset\TestCallbackCache::bar', ['testCallDisabledCacheOutput', 'arg2'] ); @@ -80,7 +74,7 @@ public function testCallDisabledCacheOutput() public function testMagicFunctionCall() { - $this->_testCall( + $this->doTestCall( __NAMESPACE__ . '\bar', ['testMagicFunctionCall', 'arg2'] ); @@ -91,37 +85,35 @@ public function testGenerateKey() $callback = __NAMESPACE__ . '\TestAsset\TestCallbackCache::emptyMethod'; $args = ['arg1', 2, 3.33, null]; - $generatedKey = $this->_pattern->generateKey($callback, $args); + $generatedKey = $this->pattern->generateKey($callback, $args); $usedKey = null; - $this->_options->getStorage()->getEventManager()->attach('setItem.pre', function ($event) use (&$usedKey) { + $this->options->getStorage()->getEventManager()->attach('setItem.pre', function ($event) use (&$usedKey) { $params = $event->getParams(); $usedKey = $params['key']; }); - $this->_pattern->call($callback, $args); + $this->pattern->call($callback, $args); $this->assertEquals($generatedKey, $usedKey); } public function testCallInvalidCallbackException() { - $this->expectException('Laminas\Cache\Exception\InvalidArgumentException'); - $this->_pattern->call(1); + $this->expectException(InvalidArgumentException::class); + $this->pattern->call(1); } public function testCallUnknownCallbackException() { - $this->expectException('Laminas\Cache\Exception\InvalidArgumentException'); - $this->_pattern->call('notExiststingFunction'); + $this->expectException(InvalidArgumentException::class); + $this->pattern->call('notExiststingFunction'); } /** * Running tests calling LaminasTest\Cache\Pattern\TestCallbackCache::bar * using different callbacks resulting in this method call */ - // @codingStandardsIgnoreStart - protected function _testCall($callback, array $args) + protected function doTestCall($callback, array $args) { - // @codingStandardsIgnoreEnd $returnSpec = 'foobar_return(' . implode(', ', $args) . ') : '; $outputSpec = 'foobar_output(' . implode(', ', $args) . ') : '; @@ -130,7 +122,7 @@ protected function _testCall($callback, array $args) ob_start(); ob_implicit_flush(0); - $return = $this->_pattern->call($callback, $args); + $return = $this->pattern->call($callback, $args); $data = ob_get_clean(); $this->assertEquals($returnSpec . $firstCounter, $return); @@ -139,11 +131,11 @@ protected function _testCall($callback, array $args) // second call - cached ob_start(); ob_implicit_flush(0); - $return = $this->_pattern->call($callback, $args); + $return = $this->pattern->call($callback, $args); $data = ob_get_clean(); $this->assertEquals($returnSpec . $firstCounter, $return); - $options = $this->_pattern->getOptions(); + $options = $this->pattern->getOptions(); if ($options->getCacheOutput()) { $this->assertEquals($outputSpec . $firstCounter, $data); } else { @@ -158,9 +150,9 @@ protected function _testCall($callback, array $args) public function testCallCanReturnCachedNullValues() { $callback = new FailableCallback(); - $key = $this->_pattern->generateKey($callback, []); - $this->_storage->setItem($key, [null]); - $value = $this->_pattern->call($callback); + $key = $this->pattern->generateKey($callback, []); + $this->storage->setItem($key, [null]); + $value = $this->pattern->call($callback); $this->assertNull($value); } } diff --git a/test/Pattern/CaptureCacheTest.php b/test/Pattern/CaptureCacheTestAbstract.php similarity index 65% rename from test/Pattern/CaptureCacheTest.php rename to test/Pattern/CaptureCacheTestAbstract.php index a0c40e40..f7913f3f 100644 --- a/test/Pattern/CaptureCacheTest.php +++ b/test/Pattern/CaptureCacheTestAbstract.php @@ -9,63 +9,60 @@ namespace LaminasTest\Cache\Pattern; use Laminas\Cache; +use Laminas\Cache\Exception\LogicException; /** * @group Laminas_Cache * @covers Laminas\Cache\Pattern\CaptureCache */ -class CaptureCacheTest extends CommonPatternTest +class CaptureCacheTestAbstract extends AbstractCommonPatternTest { - // @codingStandardsIgnoreStart - protected $_tmpCacheDir; - protected $_umask; - protected $_bufferedServerSuperGlobal; - // @codingStandardsIgnoreEnd + protected $tmpCacheDir; + protected $umask; + protected $bufferedServerSuperGlobal; protected function setUp(): void { - $this->_bufferedServerSuperGlobal = $_SERVER; - $this->_umask = umask(); + $this->bufferedServerSuperGlobal = $_SERVER; + $this->umask = umask(); - $this->_tmpCacheDir = @tempnam(sys_get_temp_dir(), 'laminas_cache_test_'); - if (! $this->_tmpCacheDir) { + $this->tmpCacheDir = @tempnam(sys_get_temp_dir(), 'laminas_cache_test_'); + if (! $this->tmpCacheDir) { $err = error_get_last(); $this->fail("Can't create temporary cache directory-file: {$err['message']}"); - } elseif (! @unlink($this->_tmpCacheDir)) { + } elseif (! @unlink($this->tmpCacheDir)) { $err = error_get_last(); $this->fail("Can't remove temporary cache directory-file: {$err['message']}"); - } elseif (! @mkdir($this->_tmpCacheDir, 0777)) { + } elseif (! @mkdir($this->tmpCacheDir, 0777)) { $err = error_get_last(); $this->fail("Can't create temporary cache directory: {$err['message']}"); } - $this->_options = new Cache\Pattern\PatternOptions([ - 'public_dir' => $this->_tmpCacheDir + $this->options = new Cache\Pattern\PatternOptions([ + 'public_dir' => $this->tmpCacheDir ]); - $this->_pattern = new Cache\Pattern\CaptureCache(); - $this->_pattern->setOptions($this->_options); + $this->pattern = new Cache\Pattern\CaptureCache(); + $this->pattern->setOptions($this->options); parent::setUp(); } public function tearDown(): void { - $_SERVER = $this->_bufferedServerSuperGlobal; + $_SERVER = $this->bufferedServerSuperGlobal; - $this->_removeRecursive($this->_tmpCacheDir); + $this->removeRecursive($this->tmpCacheDir); - if ($this->_umask != umask()) { - umask($this->_umask); + if ($this->umask != umask()) { + umask($this->umask); $this->fail("Umask wasn't reset"); } parent::tearDown(); } - // @codingStandardsIgnoreStart - protected function _removeRecursive($dir) + protected function removeRecursive($dir) { - // @codingStandardsIgnoreEnd if (file_exists($dir)) { $dirIt = new \DirectoryIterator($dir); foreach ($dirIt as $entry) { @@ -77,7 +74,7 @@ protected function _removeRecursive($dir) if ($entry->isFile()) { unlink($entry->getPathname()); } else { - $this->_removeRecursive($entry->getPathname()); + $this->removeRecursive($entry->getPathname()); } } @@ -103,25 +100,25 @@ public function testSetThrowsLogicExceptionOnMissingPublicDir() public function testSetWithNormalPageId() { - $this->_pattern->set('content', '/dir1/dir2/file'); - $this->assertFileExists($this->_tmpCacheDir . '/dir1/dir2/file'); - $this->assertSame(file_get_contents($this->_tmpCacheDir . '/dir1/dir2/file'), 'content'); + $this->pattern->set('content', '/dir1/dir2/file'); + $this->assertFileExists($this->tmpCacheDir . '/dir1/dir2/file'); + $this->assertSame(file_get_contents($this->tmpCacheDir . '/dir1/dir2/file'), 'content'); } public function testSetWithIndexFilename() { - $this->_options->setIndexFilename('test.html'); + $this->options->setIndexFilename('test.html'); - $this->_pattern->set('content', '/dir1/dir2/'); - $this->assertFileExists($this->_tmpCacheDir . '/dir1/dir2/test.html'); - $this->assertSame(file_get_contents($this->_tmpCacheDir . '/dir1/dir2/test.html'), 'content'); + $this->pattern->set('content', '/dir1/dir2/'); + $this->assertFileExists($this->tmpCacheDir . '/dir1/dir2/test.html'); + $this->assertSame(file_get_contents($this->tmpCacheDir . '/dir1/dir2/test.html'), 'content'); } public function testGetThrowsLogicExceptionOnMissingPublicDir() { $captureCache = new Cache\Pattern\CaptureCache(); - $this->expectException('Laminas\Cache\Exception\LogicException'); + $this->expectException(LogicException::class); $captureCache->get('/pageId'); } @@ -129,7 +126,7 @@ public function testHasThrowsLogicExceptionOnMissingPublicDir() { $captureCache = new Cache\Pattern\CaptureCache(); - $this->expectException('Laminas\Cache\Exception\LogicException'); + $this->expectException(LogicException::class); $captureCache->has('/pageId'); } @@ -137,7 +134,7 @@ public function testRemoveThrowsLogicExceptionOnMissingPublicDir() { $captureCache = new Cache\Pattern\CaptureCache(); - $this->expectException('Laminas\Cache\Exception\LogicException'); + $this->expectException(LogicException::class); $captureCache->remove('/pageId'); } @@ -172,26 +169,26 @@ public function testGetFilenameWithoutPublicDirAndNoPageId() public function testGetFilenameWithPublicDir() { $options = new Cache\Pattern\PatternOptions([ - 'public_dir' => $this->_tmpCacheDir + 'public_dir' => $this->tmpCacheDir ]); $captureCache = new Cache\Pattern\CaptureCache(); $captureCache->setOptions($options); $this->assertEquals( - $this->_tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/index.html'), + $this->tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/index.html'), $captureCache->getFilename('/') ); $this->assertEquals( - $this->_tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/dir1/test'), + $this->tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/dir1/test'), $captureCache->getFilename('/dir1/test') ); $this->assertEquals( - $this->_tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/dir1/test.html'), + $this->tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/dir1/test.html'), $captureCache->getFilename('/dir1/test.html') ); $this->assertEquals( - $this->_tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/dir1/dir2/test.html'), + $this->tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/dir1/dir2/test.html'), $captureCache->getFilename('/dir1/dir2/test.html') ); } @@ -201,13 +198,13 @@ public function testGetFilenameWithPublicDirAndNoPageId() $_SERVER['REQUEST_URI'] = '/dir1/test.html'; $options = new Cache\Pattern\PatternOptions([ - 'public_dir' => $this->_tmpCacheDir + 'public_dir' => $this->tmpCacheDir ]); $captureCache = new Cache\Pattern\CaptureCache(); $captureCache->setOptions($options); $this->assertEquals( - $this->_tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/dir1/test.html'), + $this->tmpCacheDir . str_replace('/', DIRECTORY_SEPARATOR, '/dir1/test.html'), $captureCache->getFilename() ); } diff --git a/test/Pattern/ClassCacheTest.php b/test/Pattern/ClassCacheTestAbstract.php similarity index 64% rename from test/Pattern/ClassCacheTest.php rename to test/Pattern/ClassCacheTestAbstract.php index a64198f6..dd53f22e 100644 --- a/test/Pattern/ClassCacheTest.php +++ b/test/Pattern/ClassCacheTestAbstract.php @@ -15,26 +15,19 @@ * @group Laminas_Cache * @covers Laminas\Cache\Pattern\ClassCache */ -class ClassCacheTest extends CommonPatternTest +class ClassCacheTestAbstract extends AbstractCommonStoragePatternTest { - // @codingStandardsIgnoreStart - /** - * @var \Laminas\Cache\Storage\StorageInterface - */ - protected $_storage; - // @codingStandardsIgnoreEnd protected function setUp(): void { - $this->_storage = new Cache\Storage\Adapter\Memory([ + $this->storage = new Cache\Storage\Adapter\Memory([ 'memory_limit' => 0 ]); - $this->_options = new Cache\Pattern\PatternOptions([ - 'class' => __NAMESPACE__ . '\TestAsset\TestClassCache', - 'storage' => $this->_storage, + $this->options = new Cache\Pattern\PatternOptions([ + 'class' => TestClassCache::class, + 'storage' => $this->storage, ]); - $this->_pattern = new Cache\Pattern\ClassCache(); - $this->_pattern->setOptions($this->_options); + $this->pattern = new Cache\Pattern\ClassCache($this->storage, $this->options); parent::setUp(); } @@ -54,7 +47,7 @@ public function getCommonPatternNamesProvider() public function testCallEnabledCacheOutputByDefault() { - $this->_testCall( + $this->doTestCall( 'bar', ['testCallEnabledCacheOutputByDefault', 'arg2'] ); @@ -62,8 +55,8 @@ public function testCallEnabledCacheOutputByDefault() public function testCallDisabledCacheOutput() { - $this->_options->setCacheOutput(false); - $this->_testCall( + $this->options->setCacheOutput(false); + $this->doTestCall( 'bar', ['testCallDisabledCacheOutput', 'arg2'] ); @@ -73,21 +66,19 @@ public function testGenerateKey() { $args = ['arg1', 2, 3.33, null]; - $generatedKey = $this->_pattern->generateKey('emptyMethod', $args); + $generatedKey = $this->pattern->generateKey('emptyMethod', $args); $usedKey = null; - $this->_options->getStorage()->getEventManager()->attach('setItem.pre', function ($event) use (&$usedKey) { + $this->options->getStorage()->getEventManager()->attach('setItem.pre', function ($event) use (&$usedKey) { $params = $event->getParams(); $usedKey = $params['key']; }); - $this->_pattern->call('emptyMethod', $args); + $this->pattern->call('emptyMethod', $args); $this->assertEquals($generatedKey, $usedKey); } - // @codingStandardsIgnoreStart - protected function _testCall($method, array $args) + protected function doTestCall($method, array $args) { - // @codingStandardsIgnoreEnd $returnSpec = 'foobar_return(' . implode(', ', $args) . ') : '; $outputSpec = 'foobar_output(' . implode(', ', $args) . ') : '; @@ -96,7 +87,7 @@ protected function _testCall($method, array $args) ob_start(); ob_implicit_flush(0); - $return = call_user_func_array([$this->_pattern, $method], $args); + $return = call_user_func_array([$this->pattern, $method], $args); $data = ob_get_clean(); $this->assertEquals($returnSpec . $firstCounter, $return); @@ -105,11 +96,11 @@ protected function _testCall($method, array $args) // second call - cached ob_start(); ob_implicit_flush(0); - $return = call_user_func_array([$this->_pattern, $method], $args); + $return = call_user_func_array([$this->pattern, $method], $args); $data = ob_get_clean(); $this->assertEquals($returnSpec . $firstCounter, $return); - if ($this->_options->getCacheOutput()) { + if ($this->options->getCacheOutput()) { $this->assertEquals($outputSpec . $firstCounter, $data); } else { $this->assertEquals('', $data); diff --git a/test/Pattern/ObjectCacheTest.php b/test/Pattern/ObjectCacheTestAbstract.php similarity index 56% rename from test/Pattern/ObjectCacheTest.php rename to test/Pattern/ObjectCacheTestAbstract.php index 0a8be5b5..56b26825 100644 --- a/test/Pattern/ObjectCacheTest.php +++ b/test/Pattern/ObjectCacheTestAbstract.php @@ -14,26 +14,18 @@ /** * @group Laminas_Cache */ -class ObjectCacheTest extends CommonPatternTest +class ObjectCacheTestAbstract extends AbstractCommonStoragePatternTest { - // @codingStandardsIgnoreStart - /** - * @var \Laminas\Cache\Storage\StorageInterface - */ - protected $_storage; - // @codingStandardsIgnoreEnd - protected function setUp(): void { - $this->_storage = new Cache\Storage\Adapter\Memory([ + $this->storage = new Cache\Storage\Adapter\Memory([ 'memory_limit' => 0 ]); - $this->_options = new Cache\Pattern\PatternOptions([ + $this->options = new Cache\Pattern\PatternOptions([ 'object' => new TestObjectCache(), - 'storage' => $this->_storage, + 'storage' => $this->storage, ]); - $this->_pattern = new Cache\Pattern\ObjectCache(); - $this->_pattern->setOptions($this->_options); + $this->pattern = new Cache\Pattern\ObjectCache($this->storage, $this->options); parent::setUp(); } @@ -53,7 +45,7 @@ public function getCommonPatternNamesProvider() public function testCallEnabledCacheOutputByDefault() { - $this->_testCall( + $this->doTestCall( 'bar', ['testCallEnabledCacheOutputByDefault', 'arg2'] ); @@ -61,8 +53,8 @@ public function testCallEnabledCacheOutputByDefault() public function testCallDisabledCacheOutput() { - $this->_options->setCacheOutput(false); - $this->_testCall( + $this->options->setCacheOutput(false); + $this->doTestCall( 'bar', ['testCallDisabledCacheOutput', 'arg2'] ); @@ -70,46 +62,46 @@ public function testCallDisabledCacheOutput() public function testCallInvoke() { - $this->_options->setCacheOutput(false); - $this->_testCall('__invoke', ['arg1', 'arg2']); + $this->options->setCacheOutput(false); + $this->doTestCall('__invoke', ['arg1', 'arg2']); } public function testGenerateKey() { $args = ['arg1', 2, 3.33, null]; - $generatedKey = $this->_pattern->generateKey('emptyMethod', $args); + $generatedKey = $this->pattern->generateKey('emptyMethod', $args); $usedKey = null; - $this->_options->getStorage()->getEventManager()->attach('setItem.pre', function ($event) use (&$usedKey) { + $this->options->getStorage()->getEventManager()->attach('setItem.pre', function ($event) use (&$usedKey) { $params = $event->getParams(); $usedKey = $params['key']; }); - $this->_pattern->call('emptyMethod', $args); + $this->pattern->call('emptyMethod', $args); $this->assertEquals($generatedKey, $usedKey); } public function testSetProperty() { - $this->_pattern->property = 'testSetProperty'; - $this->assertEquals('testSetProperty', $this->_options->getObject()->property); + $this->pattern->property = 'testSetProperty'; + $this->assertEquals('testSetProperty', $this->options->getObject()->property); } public function testGetProperty() { - $this->assertEquals($this->_options->getObject()->property, $this->_pattern->property); + $this->assertEquals($this->options->getObject()->property, $this->pattern->property); } public function testIssetProperty() { - $this->assertTrue(isset($this->_pattern->property)); - $this->assertFalse(isset($this->_pattern->unknownProperty)); + $this->assertTrue(isset($this->pattern->property)); + $this->assertFalse(isset($this->pattern->unknownProperty)); } public function testUnsetProperty() { - unset($this->_pattern->property); - $this->assertFalse(isset($this->_pattern->property)); + unset($this->pattern->property); + $this->assertFalse(isset($this->pattern->property)); } /** @@ -117,23 +109,21 @@ public function testUnsetProperty() */ public function testEmptyObjectKeys() { - $this->_options->setObjectKey('0'); - $this->assertSame('0', $this->_options->getObjectKey(), "Can't set string '0' as object key"); + $this->options->setObjectKey('0'); + $this->assertSame('0', $this->options->getObjectKey(), "Can't set string '0' as object key"); - $this->_options->setObjectKey(''); - $this->assertSame('', $this->_options->getObjectKey(), "Can't set an empty string as object key"); + $this->options->setObjectKey(''); + $this->assertSame('', $this->options->getObjectKey(), "Can't set an empty string as object key"); - $this->_options->setObjectKey(null); - $this->assertSame(get_class($this->_options->getObject()), $this->_options->getObjectKey()); + $this->options->setObjectKey(null); + $this->assertSame(get_class($this->options->getObject()), $this->options->getObjectKey()); } - // @codingStandardsIgnoreStart - protected function _testCall($method, array $args) + protected function doTestCall($method, array $args) { - // @codingStandardsIgnoreEnd $returnSpec = 'foobar_return(' . implode(', ', $args) . ') : '; $outputSpec = 'foobar_output(' . implode(', ', $args) . ') : '; - $callback = [$this->_pattern, $method]; + $callback = [$this->pattern, $method]; // first call - not cached $firstCounter = TestObjectCache::$fooCounter + 1; @@ -155,7 +145,7 @@ protected function _testCall($method, array $args) ob_end_clean(); $this->assertEquals($returnSpec . $firstCounter, $return); - if ($this->_options->getCacheOutput()) { + if ($this->options->getCacheOutput()) { $this->assertEquals($outputSpec . $firstCounter, $data); } else { $this->assertEquals('', $data); diff --git a/test/Pattern/OutputCacheTest.php b/test/Pattern/OutputCacheTestAbstract.php similarity index 58% rename from test/Pattern/OutputCacheTest.php rename to test/Pattern/OutputCacheTestAbstract.php index 697b9186..3ad82fca 100644 --- a/test/Pattern/OutputCacheTest.php +++ b/test/Pattern/OutputCacheTestAbstract.php @@ -9,53 +9,46 @@ namespace LaminasTest\Cache\Pattern; use Laminas\Cache; +use Laminas\Cache\Exception\MissingKeyException; /** * @group Laminas_Cache * @covers Laminas\Cache\Pattern\OutputCache */ -class OutputCacheTest extends CommonPatternTest +class OutputCacheTestAbstract extends AbstractCommonStoragePatternTest { - // @codingStandardsIgnoreStart - /** - * @var \Laminas\Cache\Storage\StorageInterface - */ - protected $_storage; - /** * Nesting level of output buffering used to restore on tearDown(): void * * @var null|int */ - protected $_obLevel; - // @codingStandardsIgnoreEnd + protected $obLevel; protected function setUp(): void { - $this->_storage = new Cache\Storage\Adapter\Memory([ + $this->storage = new Cache\Storage\Adapter\Memory([ 'memory_limit' => 0 ]); - $this->_options = new Cache\Pattern\PatternOptions([ - 'storage' => $this->_storage, + $this->options = new Cache\Pattern\PatternOptions([ + 'storage' => $this->storage, ]); - $this->_pattern = new Cache\Pattern\OutputCache(); - $this->_pattern->setOptions($this->_options); + $this->pattern = new Cache\Pattern\OutputCache($this->storage, $this->options); // used to reset the level on tearDown - $this->_obLevel = ob_get_level(); + $this->obLevel = ob_get_level(); parent::setUp(); } public function tearDown(): void { - if ($this->_obLevel > ob_get_Level()) { - for ($i = ob_get_level(); $i < $this->_obLevel; $i++) { + if ($this->obLevel > ob_get_Level()) { + for ($i = ob_get_level(); $i < $this->obLevel; $i++) { ob_start(); } $this->fail("Nesting level of output buffering to often ended"); - } elseif ($this->_obLevel < ob_get_level()) { - for ($i = ob_get_level(); $i > $this->_obLevel; $i--) { + } elseif ($this->obLevel < ob_get_level()) { + for ($i = ob_get_level(); $i > $this->obLevel; $i--) { ob_end_clean(); } $this->fail("Nesting level of output buffering not well restored"); @@ -78,13 +71,13 @@ public function testStartEndCacheMiss() $key = 'testStartEndCacheMiss'; ob_start(); - $this->assertFalse($this->_pattern->start($key)); + $this->assertFalse($this->pattern->start($key)); echo $output; - $this->assertTrue($this->_pattern->end()); + $this->assertTrue($this->pattern->end()); $data = ob_get_clean(); $this->assertEquals($output, $data); - $this->assertEquals($output, $this->_pattern->getOptions()->getStorage()->getItem($key)); + $this->assertEquals($output, $this->pattern->getOptions()->getStorage()->getItem($key)); } public function testStartEndCacheHit() @@ -93,10 +86,10 @@ public function testStartEndCacheHit() $key = 'testStartEndCacheHit'; // fill cache - $this->_pattern->getOptions()->getStorage()->setItem($key, $output); + $this->pattern->getStorage()->setItem($key, $output); ob_start(); - $this->assertTrue($this->_pattern->start($key)); + $this->assertTrue($this->pattern->start($key)); $data = ob_get_clean(); $this->assertSame($output, $data); @@ -104,7 +97,7 @@ public function testStartEndCacheHit() public function testThrowMissingKeyException() { - $this->expectException('Laminas\Cache\Exception\MissingKeyException'); - $this->_pattern->start(''); // empty key + $this->expectException(MissingKeyException::class); + $this->pattern->start(''); // empty key } } diff --git a/test/Pattern/PatternCacheFactoryTest.php b/test/Pattern/PatternCacheFactoryTest.php new file mode 100644 index 00000000..8dc520f2 --- /dev/null +++ b/test/Pattern/PatternCacheFactoryTest.php @@ -0,0 +1,73 @@ +factory = new PatternCacheFactory(); + $this->container = $this->createMock(ContainerInterface::class); + } + + public function testWillThrowInvalidArgumentExceptionWhenRequestedNameIsNotAFullyQualifiedClassName(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Please only use this factory for services with full qualified class names'); + ($this->factory)($this->container, 'foo'); + } + + /** + * @dataProvider builtInCachePattern + */ + public function testWillInstantiateCachePattern(string $className): void + { + $instance = ($this->factory)($this->container, $className); + self::assertInstanceOf($className, $instance); + } + + /** + * @dataProvider builtInCachePattern + */ + public function testWillInstantiateCachePatternWithOptions(string $className): void + { + + $instance = ($this->factory)($this->container, $className, [ + 'fileLocking' => false, + ]); + self::assertInstanceOf($className, $instance); + $options = $instance->getOptions(); + self::assertFalse($options->getFileLocking()); + } +} diff --git a/test/Pattern/StoragePatternCacheFactoryTest.php b/test/Pattern/StoragePatternCacheFactoryTest.php new file mode 100644 index 00000000..417acbf4 --- /dev/null +++ b/test/Pattern/StoragePatternCacheFactoryTest.php @@ -0,0 +1,135 @@ +factory = new StoragePatternCacheFactory(); + $this->container = $this->createMock(ContainerInterface::class); + } + + public function testWillThrowInvalidArgumentExceptionWhenRequestedNameIsNotAFullyQualifiedClassName(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Please only use this factory for services with full qualified class names'); + ($this->factory)($this->container, 'foo'); + } + + /** + * @dataProvider builtInCachePattern + */ + public function testWillInstantiateCachePattern(string $className): void + { + $instance = ($this->factory)($this->container, $className); + self::assertInstanceOf($className, $instance); + } + + /** + * @dataProvider builtInCachePattern + */ + public function testWillInstantiateCachePatternWithOptions(string $className): void + { + $instance = ($this->factory)($this->container, $className, [ + 'fileLocking' => false, + ]); + self::assertInstanceOf($className, $instance); + $options = $instance->getOptions(); + self::assertFalse($options->getFileLocking()); + } + + /** + * @dataProvider builtInCachePattern + */ + public function testWillInstantiateCachePatternWithOptionsAndStorage(string $className): void + { + $storage = $this->createMock(StorageInterface::class); + $instance = ($this->factory)($this->container, $className, [ + 'fileLocking' => false, + 'storage' => $storage, + ]); + self::assertInstanceOf($className, $instance); + self::assertInstanceOf(AbstractStorageCapablePattern::class, $instance); + $options = $instance->getOptions(); + self::assertFalse($options->getFileLocking()); + self::assertSame($storage, $instance->getStorage()); + } + + /** + * @dataProvider builtInCachePattern + */ + public function testWillInstantiateCachePatternWithStorageOptionAsArray(string $className): void + { + $callbackCalled = false; + $storageOptions = [ + 'name' => 'foo', + ]; + + $storage = $this->createMock(StorageInterface::class); + + $callback = function ( + ContainerInterface $container, + array $option + ) use ( + &$callbackCalled, + $storageOptions, + $storage + ): StorageInterface { + self::assertEquals($storageOptions, $option); + self::assertSame($this->container, $container); + $callbackCalled = true; + return $storage; + }; + + $factory = new StoragePatternCacheFactory($callback); + + $instance = ($factory)($this->container, $className, [ + 'storage' => $storageOptions, + ]); + + self::assertInstanceOf($className, $instance); + self::assertInstanceOf(AbstractStorageCapablePattern::class, $instance); + self::assertSame($storage, $instance->getStorage()); + self::assertTrue($callbackCalled); + } +} diff --git a/test/Pattern/TestAsset/TestCachePattern.php b/test/Pattern/TestAsset/TestCachePattern.php new file mode 100644 index 00000000..0e8ba6a3 --- /dev/null +++ b/test/Pattern/TestAsset/TestCachePattern.php @@ -0,0 +1,11 @@ +assertFalse($options->getCacheOutput()); $this->assertSame($storage, $options->getStorage()); } + + public function testHasPatternCacheFactoriesConfigured(): void + { + $container = $this->createMock(ContainerInterface::class); + $instance = new class($container) extends PatternPluginManager { + public function getFactories(): array + { + return $this->factories; + } + }; + + self::assertEquals([ + Pattern\CallbackCache::class => Pattern\StoragePatternCacheFactory::class, + Pattern\CaptureCache::class => Pattern\PatternCacheFactory::class, + Pattern\ClassCache::class => Pattern\StoragePatternCacheFactory::class, + Pattern\ObjectCache::class => Pattern\StoragePatternCacheFactory::class, + Pattern\OutputCache::class => Pattern\StoragePatternCacheFactory::class, + + // v2 normalized FQCNs + 'laminascachepatterncallbackcache' => Pattern\StoragePatternCacheFactory::class, + 'laminascachepatterncapturecache' => Pattern\PatternCacheFactory::class, + 'laminascachepatternclasscache' => Pattern\StoragePatternCacheFactory::class, + 'laminascachepatternobjectcache' => Pattern\StoragePatternCacheFactory::class, + 'laminascachepatternoutputcache' => Pattern\StoragePatternCacheFactory::class, + ], $instance->getFactories()); + } + + public function testWillPassOptionsToCachePattern(): void + { + $patternPluginManager = $this->getPluginManager(); + $patternPluginManager->setInvokableClass(TestCachePattern::class); + $options = ['cache_output' => false]; + + $instance = $patternPluginManager->build(TestCachePattern::class, $options); + self::assertInstanceOf(TestCachePattern::class, $instance); + self::assertFalse($instance->getOptions()->getCacheOutput()); + } }