Skip to content

Commit

Permalink
Merge pull request #86 from boesing/feature/storage-adapter-factory-b…
Browse files Browse the repository at this point in the history
…ackport-v2

storage adapter factory backport to v2
  • Loading branch information
boesing committed Jul 11, 2021
2 parents 7234a7f + fe70378 commit e419f8b
Show file tree
Hide file tree
Showing 20 changed files with 974 additions and 82 deletions.
30 changes: 29 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,38 @@
is able to cache different patterns (class, object, output, etc) using different
storage adapters (DB, File, Memcache, etc).


- File issues at https://github.com/laminas/laminas-cache/issues
- Documentation is at https://docs.laminas.dev/laminas-cache/

## Standalone

If this component is used without `laminas-mvc` or `mezzio`, a PSR-11 container to fetch services, adapters, plugins, etc. is needed.

The easiest way would be to use [laminas-config-aggregator](https://docs.laminas.dev/laminas-config-aggregator/) along with [laminas-servicemanager](https://docs.laminas.dev/laminas-servicemanager/).

```php
use Laminas\Cache\ConfigProvider;
use Laminas\Cache\Service\StorageAdapterFactoryInterface;
use Laminas\Cache\Storage\Adapter\Memory;
use Laminas\ConfigAggregator\ConfigAggregator;
use Laminas\ServiceManager\ServiceManager;

$config = (new ConfigAggregator([
ConfigProvider::class,
]))->getMergedConfig();

$dependencies = $config['dependencies'];

$container = new ServiceManager($dependencies);

/** @var StorageAdapterFactoryInterface $storageFactory */
$storageFactory = $container->get(StorageAdapterFactoryInterface::class);

$storage = $storageFactory->create(Memory::class);

$storage->setItem('foo', 'bar');
```

## Benchmarks

We provide scripts for benchmarking laminas-cache using the
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"require-dev": {
"laminas/laminas-cli": "^1.0",
"laminas/laminas-coding-standard": "~1.0.0",
"laminas/laminas-config-aggregator": "^1.5",
"laminas/laminas-serializer": "^2.6",
"phpbench/phpbench": "^1.0.0-beta2",
"phpspec/prophecy-phpunit": "^2.0",
Expand Down
30 changes: 30 additions & 0 deletions docs/book/basic-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Basic Usage

## Standalone

If this component is used without `laminas-mvc` or `mezzio`, a PSR-11 container to fetch services, adapters, plugins, etc. is needed.

The easiest way would be to use [laminas-config-aggregator](https://docs.laminas.dev/laminas-config-aggregator/) along with [laminas-servicemanager](https://docs.laminas.dev/laminas-servicemanager/).

```php
use Laminas\Cache\ConfigProvider;
use Laminas\Cache\Service\StorageAdapterFactoryInterface;
use Laminas\Cache\Storage\Adapter\Memory;
use Laminas\ConfigAggregator\ConfigAggregator;
use Laminas\ServiceManager\ServiceManager;

$config = (new ConfigAggregator([
ConfigProvider::class,
]))->getMergedConfig();

$dependencies = $config['dependencies'];

$container = new ServiceManager($dependencies);

/** @var StorageAdapterFactoryInterface $storageFactory */
$storageFactory = $container->get(StorageAdapterFactoryInterface::class);

$storage = $storageFactory->create(Memory::class);

$storage->setItem('foo', 'bar');
```
51 changes: 27 additions & 24 deletions docs/book/psr16.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ operations.
Instantiation is as follows:

```php
use Laminas\Cache\StorageFactory;
use Laminas\Cache\Psr\SimpleCache\SimpleCacheDecorator;
use Laminas\Cache\Service\StorageAdapterFactoryInterface;
use Psr\Container\ContainerInterface;

$storage = StorageFactory::factory([
'adapter' => 'apc',
]);
/** @var ContainerInterface $container */
$container = null; // can be any configured PSR-11 container

$storageFactory = $container->get(StorageAdapterFactoryInterface::class);
$storage = $storageFactory->create('apc');

$cache = new SimpleCacheDecorator($storage);
```
Expand Down Expand Up @@ -85,14 +88,25 @@ We provide a number of examples of [attaching plugins to storage adapters in the
plugins chapter](storage/plugin.md). Generally, it will be one of:

```php
use Laminas\Cache\Service\StorageAdapterFactoryInterface;
use Laminas\Cache\Storage\Plugin\Serializer;
use Psr\Container\ContainerInterface;

// Manual attachment after you have an instance:
$cache->addPlugin(new Serializer());

// Via configuration:
$cache = StorageFactory::factory([
'adapter' => 'filesystem',
'plugins' => [['name' => 'serializer']],
]);
/** @var ContainerInterface $container */
$container = null; // can be any configured PSR-11 container

$storageFactory = $container->get(StorageAdapterFactoryInterface::class);
$cache = $storageFactory->create(
'filesystem',
[],
[
['name' => 'serializer'],
]
);
```

## Deleting Items and Exceptions
Expand All @@ -105,19 +119,8 @@ Generally, laminas-cache storage adapters comply with this. However, it is possi
to configure your adapter such that you may get a false positive result from
these methods.

When an exception is raised and caught during key removal by an adapter, the
adapter triggers an event with a `Laminas\Cache\Storage\ExceptionEvent`. Plugins
can react to these, and even manipulate the event instance. One such plugin,
`Laminas\Cache\Storage\Plugin\ExceptionHandler`, has a configuration option,
`throw_exceptions` that, when boolean `false`, will prevent raising the
exception. In such cases, adapters will typically return a boolean `false`
anyways, but custom, third-party adapters may not.

Additionally, if you add a custom plugin that listens to removal event
exceptions and modifies the return value and/or disables throwing the exception,
a false positive return value could occur.

As such, we recommend that if you wish to use laminas-cache to provide a PSR-16
adapter, you audit the plugins you use with your adapter to ensure that you will
get consistent, correct behavior for `delete()` and `deleteMultiple()`
operations.
When an exception is raised and caught during key removal by an adapter, the adapter triggers an event with a `Laminas\Cache\Storage\ExceptionEvent`. Plugins can react to these, and even manipulate the event instance. One such plugin, `Laminas\Cache\Storage\Plugin\ExceptionHandler`, has a configuration option, `throw_exceptions` that, when boolean `false`, will prevent raising the exception. In such cases, adapters will typically return a boolean `false` anyways, but custom, third-party adapters may not.

Additionally, if you add a custom plugin that listens to removal event exceptions and modifies the return value and/or disables throwing the exception, a false positive return value could occur.

As such, we recommend that if you wish to use laminas-cache to provide a PSR-16 adapter, you audit the plugins you use with your adapter to ensure that you will get consistent, correct behavior for `delete()` and `deleteMultiple()` operations.
40 changes: 23 additions & 17 deletions docs/book/psr6.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@ compliant wrapper for supported storage adapters.
PSR-6 specifies a common interface to cache storage, enabling developers to switch between implementations without
having to worry about any behind-the-scenes differences between them.


## Quick Start

To use the pool, instantiate your storage as normal, then pass it to the
`CacheItemPoolDecorator`.

```php
use Laminas\Cache\StorageFactory;
use Laminas\Cache\Psr\CacheItemPool\CacheItemPoolDecorator;
use Laminas\Cache\Psr\CacheItemPool\CacheItemPoolDecorator;use Laminas\Cache\Service\StorageAdapterFactoryInterface;use Psr\Container\ContainerInterface;

/** @var ContainerInterface $container */
$container = null; // can be any configured PSR-11 container

$storageFactory = $container->get(StorageAdapterFactoryInterface::class);

$storage = StorageFactory::factory([
'adapter' => 'apc',
]);
$storage = $storageFactory->create('apc');

$pool = new CacheItemPoolDecorator($storage);

Expand All @@ -47,7 +48,6 @@ Note that you will always get back a `CacheItem` object, whether it was found in
values like an empty string, `null`, or `false` can be stored. Always check `isHit()` to determine if the item was
found.


## Supported Adapters

The PSR-6 specification requires that the underlying storage support time-to-live (TTL), which is set when the
Expand All @@ -73,7 +73,6 @@ actually saved to storage. If this is set when you instantiate the pool it will
`Psr\Cache\CacheException`. Changing the setting after you have instantiated the pool will result in non-standard
behaviour.


## Logging Errors

The specification [states](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-6-cache.md#error-handling):
Expand All @@ -91,8 +90,11 @@ way. Doing so is as simple as adding an [`ExceptionHandler` plugin](storage/plug
[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) compliant logger
called `$logger`:


```php
use Laminas\Cache\Psr\CacheItemPool\CacheItemPoolDecorator;
use Laminas\Cache\Service\StorageAdapterFactoryInterface;
use Psr\Container\ContainerInterface;

$cacheLogger = function (\Exception $e) use ($logger) {
$message = sprintf(
'[CACHE] %s:%s %s "%s"',
Expand All @@ -103,27 +105,32 @@ $cacheLogger = function (\Exception $e) use ($logger) {
);
$logger->error($message);
};
}
$storage = StorageFactory::factory([
'adapter' => 'apc',
'plugins' => [

/** @var ContainerInterface $container */
$container = null; // can be any configured PSR-11 container

$storageFactory = $container->get(StorageAdapterFactoryInterface::class);

$storage = $storageFactory->create(
'apc',
[],
[
[
'name' => 'exceptionhandler',
'options' => [
'exception_callback' => $cacheLogger,
'throw_exceptions' => true,
],
],
],
]);
]
);

$pool = new CacheItemPoolDecorator($storage);
```

Note that `throw_exceptions` should always be `true` (the default) or you will not get the correct return values from
calls on the pool such as `save()`.


## Supported Data Types

As per [the specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-6-cache.md#data), the
Expand All @@ -133,4 +140,3 @@ returned as a value with exactly the same type.
Not all adapters can natively store all these types. For instance, Redis stores booleans and integers as a string. Where
this is the case *all* values will be automatically run through `serialize()` on save and `unserialize()` on get: you
do not need to use a `Laminas\Cache\Storage\Plugin\Serializer` plugin.

77 changes: 50 additions & 27 deletions docs/book/storage/adapter.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,38 +14,46 @@ Configuration is handled by either `Laminas\Cache\Storage\Adapter\AdapterOptions
or an adapter-specific options class if it exists. You may pass the options
instance to the class at instantiation, via the `setOptions()` method, or,
alternately, pass an associative array of options in either place (internally,
these are then passed to an options class instance). Alternately, you can pass
either the options instance or associative array to the
`Laminas\Cache\StorageFactory::factory` method.
these are then passed to an options class instance). Alternately, you can pass associative array to the
`Laminas\Cache\Service\StorageAdapterFactoryInterface::create` method.

## Quick Start

Caching adapters can either be created from the provided
`Laminas\Cache\StorageFactory`, or by instantiating one of the
`Laminas\Cache\Service\StorageAdapterFactoryInterface`, or by instantiating one of the
`Laminas\Cache\Storage\Adapter\*` classes. To make life easier, the
`Laminas\Cache\StorageFactory` comes with a `factory()` method to create an adapter
`Laminas\Cache\Service\StorageAdapterFactoryInterface` comes with a `create()` method to create an adapter
and all requested plugins at once.

```php
use Laminas\Cache\StorageFactory;
use Laminas\Cache\Service\StorageAdapterFactoryInterface;
use Laminas\Cache\Service\StoragePluginFactoryInterface;
use Psr\Container\ContainerInterface;

/** @var ContainerInterface $container */
$container = null; // can be any configured PSR-11 container

/** @var StorageAdapterFactoryInterface $storageFactory */
$storageFactory = $container->get(StorageAdapterFactoryInterface::class);

// Via factory:
$cache = StorageFactory::factory([
'adapter' => 'apc',
'options' => ['ttl' => 3600],
'plugins' => [
$cache = $storageFactory->create(
'apc',
['ttl' => 3600],
[
[
'name' => 'exception_handler',
'options' => [
'throw_exceptions' => false,
],
],
],
]);
]
);

// Alternately, create the adapter and plugin separately:
$cache = StorageFactory::adapterFactory('apc', ['ttl' => 3600]);
$plugin = StorageFactory::pluginFactory('exception_handler', [
$cache = $storageFactory->create('apc', ['ttl' => 3600]);
$pluginFactory = $container->get(StoragePluginFactoryInterface::class);
$plugin = $pluginFactory->create('exception_handler', [
'throw_exceptions' => false,
]);
$cache->addPlugin($plugin);
Expand Down Expand Up @@ -1117,20 +1125,28 @@ Capability | Value
### Basic Usage

```php
use Laminas\Cache\StorageFactory;
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::factory([
'adapter' => 'filesystem',
'plugins' => [
$cache = $storageFactory->create(
'filesystem',
[],
[
// Don't throw exceptions on cache errors
[
'name' => 'exception_handler',
'options' => [
'throw_exceptions' => false
],
],
],
]);
]
);

$key = 'unique-cache-key';
$result = $cache->getItem($key, $success);
Expand All @@ -1143,17 +1159,24 @@ if (! $success) {
### Get multiple Rows from a Database

```php
use Laminas\Cache\StorageFactory;
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);

// Instantiate the cache instance using a namespace for the same type of items
$cache = StorageFactory::factory([
'adapter' => 'filesystem',
$cache = $storageFactory->create(
'filesystem',
// With a namespace, we can indicate the same type of items,
// so we can simply use the database id as the cache key
'options' => [
[
'namespace' => 'dbtable',
],
'plugins' => [
[
// Don't throw exceptions on cache errors
[
'name' => 'exception_handler',
Expand All @@ -1165,8 +1188,8 @@ $cache = StorageFactory::factory([
[
'name' => 'Serializer',
],
],
]);
]
);

// Load two rows from cache if possible
$ids = [1, 2];
Expand Down
Loading

0 comments on commit e419f8b

Please sign in to comment.