Code Monkey home page Code Monkey logo

laminas-cache's Introduction

laminas-cache

Build Status

🇷🇺 Русским гражданам

Мы, участники Laminas, родились и живем в разных странах. У многих из нас есть друзья, родственники и коллеги как в России, так и в Украине. Некоторые из нас родились в России. Некоторые из нас живут в России. У некоторых бабушки и дедушки сражались с фашистами во Второй мировой войне. Здесь никто не поддерживает фашизм.

У одного из нас есть украинская родственница, которая спаслась из дома вместе с сыном. Поезд задержался из-за бомбежки на дороге впереди. У нас есть друзья, которые прячутся в бомбоубежищах. Мы с тревогой ждем весточки от них после воздушных налетов, которые беспорядочно наносят удары и попадают по больницам, школам, детским садам и домам. Мы не берем это из каких-либо СМИ. Мы наблюдаем это напрямую.

Вы доверяете нам достаточно, чтоб использовать наши программы, и мы просим вас довериться нам вновь. Мы нуждаемся в помощи. Выходите и протестуйте против этой бесполезной войны. Остановите кровопролитие. Скажите "Нет войне!"

🇺🇸 To Citizens of Russia

We at Laminas come from all over the world. Many of us have friends, family and colleagues in both Russia and Ukraine. Some of us were born in Russia. Some of us currently live in Russia. Some have grandparents who fought Nazis in World War II. Nobody here supports fascism.

One team member has a Ukrainian relative who fled her home with her son. The train was delayed due to bombing on the road ahead. We have friends who are hiding in bomb shelters. We anxiously follow up on them after the air raids, which indiscriminately fire at hospitals, schools, kindergartens and houses. We're not taking this from any media. These are our actual experiences.

You trust us enough to use our software. We ask that you trust us to say the truth on this. We need your help. Go out and protest this unnecessary war. Stop the bloodshed. Say "stop the war!"

Laminas\Cache provides a general cache system for PHP. The Laminas\Cache component is able to cache different patterns (class, object, output, etc) using different storage adapters (DB, File, Memcache, etc).

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 along with laminas-servicemanager.

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 PHPBench framework; these can be found in the benchmark/ directory of each storage adapter.

To execute the benchmarks you can run the following command:

$ vendor/bin/phpbench run --report=aggregate

laminas-cache's People

Contributors

akrabat avatar bakura10 avatar boesing avatar brettmc avatar cgmartin avatar dasprid avatar evandotpro avatar ezimuel avatar freeaqingme avatar froschdesign avatar koopzington avatar maks3w avatar marc-mabe avatar michalbundyra avatar micheh avatar mikaelkael avatar mwillbanks avatar ocramius avatar pdeszynski avatar prolic avatar ralphschindler avatar renovate[bot] avatar samsonasik avatar sgehrig avatar stefanotorresi avatar thinkscape avatar thomasvargiu avatar vahid-sohrabloo avatar wdalmut avatar weierophinney avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

laminas-cache's Issues

Component split

I would like to get your opinions about splitting zend-cache into different components.
The reason for that is simply a testing and dependency hell.

  • It's not possible to define a required extension in composer.json so all adapters have to manually check if the current environment is matching the requirements
  • Testing against all adapters is a nightmare and takes very long.
    • The travis.yml file al already very complex but it still doesn't test all common cases.
    • Some extensions are missing or not 100% compatible for PHP-7 which makes things here more complicated.
    • Doesn't test against different (most common) extension versions
    • Nearly impossible to run tests for ZendServer adapters
  • another reason is that normally you only need 1 or 2 adapters but that's not very important as zend-cache doesn't install so many files per adapter.

In my opinion it makes sense to split parts into it's own repository as long as the part requires a non standard extension or another currently optional dependency.

Thoughts ?

This is a structure how this could look like:

zendframework/zend-cache

  • Exceptions
  • Patterns
  • Service
  • Factories
  • Storage Interfaces
  • Storage Plugins
    • all except the Serializer plugin
  • Storage Adapters
    • BlackHole
    • Filesystem
    • Memory

zendframework/zend-cache-serializer

  • adds the serializer storage plugin which requires zend-serializer

zendframework/zend-cache-apc

  • adds the Apc storage adapter requires the apc extension (or compatible extension)

zendframework/zend-cache-apcu

  • adds the Apcu storage adapter requires the apcu extension

zendframework/zend-cache-dba

  • adds the Dba storage adapter requires the dba extension

zendframework/zend-cache-memcache

  • adds the Memcache storage adapter requires the memcache extension

zendframework/zend-cache-memcached

  • adds the Memcached storage adapter requires the memcached extension

zendframework/zend-cache-mongo

  • adds the Mongo storage adapter requires the mongo extension
  • It's currently the MongoDb adapter but there is also another extension called mongodb

zendframework/zend-cache-redis

  • adds the Redis storage adapter requires the redis extension

zendframework/zend-cache-session

  • adds the Session storage adapter requires the zend-session

zendframework/zend-cache-wincache

  • adds the WinCache storage adapter requires the wincache extension

zendframework/zend-cache-xcache

  • adds the XCache storage adapter requires the xcache extension

zendframework/zend-cache-zendserver

  • adds the ZendDataCacheDisk and ZendDataCacheShm storage adapter requires the zend data cache extension
  • This is currently called ZendServerDisk / ZendServerShm because the extension is part of Zend Server

Originally posted by @marc-mabe at zendframework/zend-cache#93

`Serializer` plugin does increment value in decrement listener

Bug Report

Q A
Version(s) 2.11.1

Summary

When using StorageInterface::decrementItem in combination with the Serializer Plugin, the Serialzer plugin will actually increment the stored value while returning the decremented value.

Current behavior

Given the AbstractCommonAdapterTest::testDecrementItem, all adapters fail here when the Serializer-Plugin is used.

$oldValue = $storage->getItem($params['key'], $success, $casToken);
$newValue = $oldValue - $params['value']; // decremented

if ($success) {
    $storage->checkAndSetItem($casToken, $params['key'], $oldValue + $params['value']); // <- incremented
    $result = $newValue;
} else {
    $result = false;
}

$event->stopPropagation(true);
return $result;

How to reproduce

public function testDecrementItem(): void
{
    $this->assertTrue($this->storage->setItem('counter', 30));
    $this->assertEquals(25, $this->storage->decrementItem('counter', 5));
    $this->assertEquals(25, $this->storage->getItem('counter'));
}

Expected behavior

The AbstractCommonAdapter::testDecrementItem-Test will pass.

Add storage adapter for YAC

An extreme fast shared memory cache extension YAC.
It's still in beta and it's not sure if this extension ever reaches it's beta state as it doesn't have a locking mechanism which leads a possibility to retrieve wrong data.

However it seems like this extension is already very popular and alternatives like APCu are still in beta, too.


Originally posted by @marc-mabe at zendframework/zend-cache#41

Normalize adapter configuration

Feature Request

Q A
New Feature yes
BC Break yes (in 3.0)/no (in 2.x, just deprecating it)

Summary

Specify Adapter Configuration

There are multiple specifications of configurations in laminas-cache v2 to create an adapter with plugins and options.

These specifications are:

<?php

use Laminas\Cache\Storage\Adapter\Memory;
use Laminas\Cache\Storage\Plugin\IgnoreUserAbort;
use Laminas\Cache\StorageFactory;

StorageFactory::factory([
    'adapter' => 'memory',
    'options' => [],
    'plugins' => ['Serializer'],
]);

StorageFactory::factory([
    'adapter' => [
        'name' => Memory::class,
        'options' => [],
    ],
    'options' => [], // Yes! These are getting merged with those from `adapter.options` ...
    'plugins' => [
        IgnoreUserAbort::class => ['exitOnAbort' => true],
    ],
]);

StorageFactory::factory([
    'adapter' => Memory::class,
    'options' => [],
    'plugins' => [
        [
            'name' => IgnoreUserAbort::class,
            'options' => ['exitOnAbort' => true],
            'priority' => PHP_INT_MAX,
        ],
    ],
]);

In v3, we should limit the specification to one configuration which can be used with the upcoming StorageAdapterFactory.

use Laminas\Cache\Storage\Adapter\Memory;
use Laminas\Cache\Storage\Plugin\IgnoreUserAbort;

[
    'adapter' => Memory::class,
    'options' => [],
    'plugins' => [
        [
            'name' => IgnoreUserAbort::class,
            'options' => ['exitOnAbort' => true],
            'priority' => PHP_INT_MAX,
        ],
    ],
];

The psalm notation of this configuration would look like this:

/**
 * @psalm-type InternalOptionalPriorityConfigurationType = array{priority?:int}
 * @psalm-type InternalPluginArrayConfigurationType = array{name:string,options?:array<string,mixed>}
 * @psalm-type PluginArrayConfigurationWithPriorityType = InternalPluginArrayConfigurationType&InternalOptionalPriorityConfigurationType
 * @psalm-type StorageAdapterArrayConfigurationType = array{
 *     name: non-empty-string,
 *     options?: array<string,mixed>,
 *     plugins?: list<PluginArrayConfigurationWithPriorityType>
 * }
 */

To help users migrate their existing configurations, we might want to provide a laminas-cli command to migrate from the old structure to the new structure.

Zend Data Cache: APC compatibility broken

The Zend Data Cache comes with a compatibility layer for APC but the APC adapter doesn't work with it:

curl -sS -v 'http://localhost/test-zend-cache.php'
* About to connect() to localhost port 80 (#0)
*   Trying ::1...
* Connected to localhost (::1) port 80 (#0)
> GET /test-zend-cache.php HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Wed, 17 Feb 2016 21:08:54 GMT
< Server: Apache/2.4.6 (CentOS) PHP/5.6.17
< X-Powered-By: PHP/5.6.15 ZendServer/8.5.2
< ZRay-ID: 0@3936@1455741553@0
< Set-Cookie: ZDEDebuggerPresent=php,phtml,php3; path=/
< Content-Length: 81
< Content-Type: text/html; charset=UTF-8
< 
.............E.......E..EE..E.E.E.....E......E.FF.F.FF......SEEE. 65 / 72 ( 90%)

I currently don't get the full result. Could be a segmentation fault somewhere but it's a bit more work as the Zend Data Cache doesn't run in CLI and this bug is not very impotent.

-> For now if you are using the Zend Data Cache than please use one of the ZendServer[Disk|Shm] adapters.


Originally posted by @marc-mabe at zendframework/zend-cache#78

PHP 8 support for v2.x

While many laminas components seem to support PHP 8 by now, laminas-cache is currently preventing us from upgrading to PHP 8. I tried to require "laminas/laminas-cache": "^3.0@dev", but composer update then fails with laminas/laminas-cache-storage-adapter-apc 1.0.1 requires php ^5.6 || ^7.0. I only need the redis adapter btw, but laminas-cache requires all adapters.

Is there an ETA for PHP 8 support or should I look for alternatives of laminas-cache?

PSR-6: Deferred items should expire aswell as persisted items

Bug Report

Q A
Version(s) 2.10.0

Summary

During implementation of #57, I played around with cache/integration-tests and got a failed test

Current behavior

Test is failing.

How to reproduce

Use APCu storage for integration test. I'm not sure why it states it is not compatible with deferred items as it definitely is.
The current implementation of the decorator does not verify the expiry.
It stores the value as TTL which in fact does not change after calculated.

Expected behavior

Deferred items should expire aswell. The decorator has to calculate the TTL prior storing to cache.
The usage of TTL should be avoided within the CacheItem at all.

`Serializer` plugin does not serialize `token` argument for `checkAndSetItem`

Bug Report

Q A
Version(s) 2.13.0

Summary

When a cache storage is used with the Serializer plugin, the Serializer plugin is pre-processing arguments for further usage.
When checkAndSetItem is used, the Serializer plugin is listening on the checkAndSetItem.pre event and delegates the arguments to the onWriteItemPre handler. This handler is re-used from replaceItem(s), addItem(s) and setItem(s).
None of these methods have a token argument and thus, it is not pre-processing the token at all, which results into an invalid assertion in the AbstractAdapter#internalCheckAndSetItem or by the method implemented by a storage.

protected function internalCheckAndSetItem(& $token, & $normalizedKey, & $value)
{
    $oldValue = $this->internalGetItem($normalizedKey);
    if ($oldValue !== $token) {
        return false;
    }

    return $this->internalSetItem($normalizedKey, $value);
}

Current behavior

Every oldValue with token comparison fails due to unserialized token vs serialized oldValue (due to the usage of internalGetItem rather than getItem).

How to reproduce

    public function testCanCompareOldValueWithTokenWhenUsedWithSerializerPlugin(): void
    {
        $storage = $this->getMockForAbstractAdapter();
        $storage
            ->addPlugin(new Serializer());

        $storage
            ->expects(self::once())
            ->method('internalGetItem')
            ->with('foo')
            ->willReturn(serialize('bar'));

        self::assertTrue($storage->checkAndSetItem('bar', 'foo', 'baz'));
    }

Expected behavior

Assertion passes. The Serializer plugin should convert the token the same way as it does for the value. I am experiencing this with the RedisCluster adapter at the moment but this might be the case for other adapters as well. To workaround, I'll use RedisCluster::OPT_SERIALIZER instead and thus, I just create this Issue while not fixing it as this is the behavior since at least many years.

Return values of some `AbstractAdapter` methods do not reflect the annotated return type

Bug Report

There is wrong return type in

zend-cache/src/Storage/Adapter/AbstractAdapter.php

/**
 * Remove an item.
 *
 * @param  string $key
 * @return bool
 * @throws Exception\ExceptionInterface
 *
 * @triggers removeItem.pre(PreEvent)
 * @triggers removeItem.post(PostEvent)
 * @triggers removeItem.exception(ExceptionEvent)
 */
public function removeItem($key)

Expected behavior

There should be @return mixed

There is calling internally
return $this->triggerPost(FUNCTION, $args, $result);

which is returnng mixed.

Idea: add getItemWithCallback method

Implementation could looks like

function getItemWithCallback($key, $callback) {
   $item = $this->getItem($key);
   if ($item === false) {
       $item = $callback($key);
       $this->setItem($key, $item);
   }
   return $item;
}

And with APCu 5.1, this can be override to use new "apcu_entry" API which will manage a lock to avoid bad run race condition, and a single execution of the callback.


Originally posted by @remicollet at zendframework/zend-cache#49

`DeprecatedStorageFactoryConfigurationCheckCommand` leads to issues when using `laminas-test` `AbstractHttpControllerTestCase`

With v2.12.0, we introduced a laminas-cli command to verify configurations in existing projects so the cache configuration is not outdated.

By using the NAME constant from the command class to receive the commands name (so we stay in sync it it should get renamed), the class has to be autoloaded. This class extends the non-existing command class from symfony/console which then leads to an error.

We can ensure that the command is not added to the configuration when laminas-cli is not loaded.

I suggest checking for the existence of Symfony\Console\Command\Command class and if that exists, we pass the command to the configuration.


laminas-cache provides a command to verify if a project contains deprecated configuration styles regarding cache.

However, the command is only added to the laminas-cli configuration along with the factory. It should not be used in any case until laminas-cli is installed and vendor/bin/laminas is executed.

Originally posted by @boesing in laminas/laminas-test#38 (comment)

Deprecate non-`StorageInterface` `storage` option for `PatternOptions`

Feature Removal

Q A
Deprecation yes

Summary

Due to the fact that every storage adapter has to register itself to the AdapterPluginManager, a string option for the PatternOptions wont be supported in laminas-cache v3.0.

Starting with v2.12, a deprecation error is triggered when the PatternOptions are instantiated with the storage option being a string rather than StorageInterface.
If a project uses the PatternPluginManager, the plugin manager will ensure that only StorageInterface instances are passed as the storage option.

Starting with v3.0, the PatternOptions::setStorage will only accept StorageInterface instances and thus, instantiations with storage as a string value will lead to a type error.

Cache adapter compatibility with v3

This issue is to keep track on which cache adapters are ready for 3.0

Affected components

Component v3 compatible
laminas/laminas-cache-storage-adapter-apcu
laminas/laminas-cache-storage-adapter-blackhole
laminas/laminas-cache-storage-adapter-ext-mongodb
laminas/laminas-cache-storage-adapter-filesystem
laminas/laminas-cache-storage-adapter-memcached
laminas/laminas-cache-storage-adapter-memory
laminas/laminas-cache-storage-adapter-redis
laminas/laminas-cache-storage-adapter-session

[RFC]: Support for PSR-6 & PSR-16 v3

Feature Request

Q A
New Feature yes
BC Break no

Summary

PSR-6 and PSR-16 has v2 & v3 available. Since I dont see the point of supporting v2 AND v3, we should be fine with supporting v1 and v3.
We could use composer/deprecated-package-versions along with an autoload check for the psr/cache version to use class_alias to provide support for both v1 & v3 of psr/cache and psr/simple-cache.

Same compatibility layer is used in laminas/laminas-cli to support both symfony/console v4 & v5.

We could also use composer-runtime-api requirement but I am not yet fully convinced that anyone is able to use composer in v2 but I am open for this change, if the majority is fine with having a hard requirement to the composer-runtime-api.

Version 3 should be useable without specific DI container

Feature Request

This library should be usable standalone when attempting to create instances of caching objects.

Q A
New Feature yes
RFC yes
BC Break no

Summary

The static factories StorageFactory and PatternFactory are deprecated in the latest V2.12, however their use case does not seem to be recreated properly in alternative methods.

The way I think I'm supposed to consume this library in V3 is that I have to use either Mezzio or Laminas MVC because they provide a means to consume the output of the ConfigProvider class - which is very Laminas-specific configuration code that will not run on its own or explain by itself what has to be done in order to create a class that is able to do caching.

My use case: I have multiple applications, which use about any possible DI container, for example PHP-DI, Pimple or Laminas. I cannot rule out homebrew solutions as well, and possibly no DI in very old applications.

These applications have access to quite a number of libraries that access HTTP-based services (SOAP and REST), so caching them is one required feature. And each of these library has a static factory call that anybody can use to get a working instance of the service client. I intentionally did not want to tie these libraries into any DI container, i.e. I am unable to "just use one DI everywhere", so instead I opted for an interface that any DI container is able to access.

Going one step deeper, these client factories have to include the caching layer, and for that they are calling another static factory of mine, that is eventually calling the StorageFactory mentioned above. This acts mostly as a convenience layer.

I would be able to add any dependency injection required for Laminas to build StorageInterface instances in the future, however I have no clue how to do this without being required to effectively grab quite an amount of otherwise unused dependencies unrelated to the task, build one of the mentioned DI containers by injecting the configuration, and then get a working instance. Something that used to be a simple static call with some carefully prepared configuration array.

I do understand removing legacy waste is an issue, and I have no intention of reverting the deprecation, but there is some information gap right now, and maybe functionality gap as well.

Currently I cannot "inject StorageAdapterFactoryInterface" because the implementing class StorageAdapterFactory requires two dependencies, both of them having non-trivial constructor parameters themselves.

The StorageAdapterFactoryFactory also doesn't help because it requires a properly configured container.

Regular Expression to validate key length leads to compilation error

\Laminas\Cache\Storage\Adapter\Redis was upgraded to have maxKeyLength capability based on the server version. It now indicates 512000000

When using a SimpleCacheDecorator with this kind of adapter the validateKey method will generate PHP warning because of the way the key length is done.

preg_match(): Compilation failed: number too big in {} quantifier at offset 9").

Mark plugin manager objects final

Feature Request

Q A
New Feature yes
BC Break yes

Summary

In v3, we might want to mark plugin managers as final to avoid having the same problems as in v2 where we had to support laminas-servicemanager v2 and v3 and thus had to create these polyfill classes which were not marked as internal or final. The latter wasn't possible as the initial plugin manager wasn't final either.

So in v2, we have to keep those polyfills just because they were not marked as internal or final.

This brings up another flaw in laminas-servicemanager as it lacks the ability to have an PluginManagerInterface which provides the PluginManagerInterface::build method.

I've already mentioned that in discourse but there was no answer since 2020/11.

As at least one of those plugin managers are used to provide adapters via delegator factories, this should be done for v3.

Plugin Managers to finalize

  1. PatternPluginManager (for patterns)
  2. AdapterPluginManager (for adapters)
  3. PluginManager (for storage plugins)

`CacheItemPoolDecorator::commit` could use `StorageInterface::setItems` for items with same TTL

Feature Request

Q A
New Feature yes
RFC yes
BC Break no

Summary

As we are using StorageInterface internally, we could take advantage of it by using StorageInterface::setItems for items which share the same TTL.

This would speed up processing deferred items when using backends which provide appropriate features.

Lets take Redis as an example, which offers the mSet or the transactional setex logic (as used in laminas/laminas-cache-storage-adapter-redis).

There are plenty of other implementations which can store multiple values at once. Thus, I think this addition could make some difference.

Feedback welcome.

Adapters must provide the maximum supported key length

Feature Request

Q A
New Feature yes
BC Break yes

Summary

I'd like to introduce a new AdapterOptions method which provides the maximum key length an adapter supports.
While implementing PHP 8.0 for the memcache adapter we found out, that the SimpleCacheDecorator checks for a maximum of 64 characters for the key.

The PSR-16 standard states, that a cache storage must support at least 64 characters but may support longer ones.
So instead of blocking keys longer than 64 characters, we should block keys longer than the maximum limit of the underlying cache adapter.

So in the end, the SimpleCacheDecorator can be improved based on this addition.

Documentation: Add integration examples for `mezzio` and `laminas-mvc`

Documentation Improvements

Summary

We do actually lacking examples on how to integrate this library into laminas-mvc or mezzio.
One of our users found an old blog post written by @samsonasik in 2013 (pointed me on that via Slack).

Luckily, I've chose that configuration style (just the plugins config is not compatible with v3.0) and thus that helped the user.
I would prefer having these examples as part of our documentation.

TL;DR

Add examples on how to implement caches configuration for the StorageCacheAbstractServiceFactory and cache configuration for the StorageCacheFactory.

[ZF3] Split out resource factories / manager

One of the most confusion part of the current cache component is the usage of resource manager.
The resource managers are used to instantiate resources (like an instance of Memcache/d) and to share the same resources over different instances of the fitting interface. Sharing resource is required to not open a new resource (like a network connection) to the same thing only to be able to use different storage instances with different options (like namespace / TTL).

In my opinion the cache adapters should not contain so much complexity and I would like to remove this functionality and simply let inject the resources into the adapters if required.

But where move this functionality to?

  • Should this be part of another component like zend-servicemanager?
  • Should this be done completely by the user?

Related:

@weierophinney @ezimuel @Maks3w @Ocramius Thoughts?


Originally posted by @ghost at zendframework/zend-cache#24

[ZF3] Remove StorageFactory and PatternFactory

Hey there,

I would like to drop those abstract factories in favor of using the related PluginManagers directly.
Since #93 introduces external packages, it will get more and more difficult to attach plugins/storage adapters to the PluginManagers, if not retrieved by the projects ContainerInterface.

Imho, each external package should provide their own factories for their plugins/storage adapters and just inject these to the related PluginManager (e.g. by using delegators? Some feedback would be great).

The original idea of @marc-mabe was to use the Module object to add aliases and factories to the PluginManager but as of zend-config-aggregator or in projects without zend-mvc, this wont work anymore.

I would appreciate to get some feedback on this.
@weierophinney


Originally posted by @boesing at zendframework/zend-cache#175

PHP 8.0 support

Feature Request

Q A
New Feature yes

Summary

To be prepared for the december release of PHP 8.0, this repository has some additional TODOs to be tested against the new major version.

In order to make this repository compatible, one has to follow these steps:

  • Modify composer.json to provide support for PHP 8.0 by adding the constraint ~8.0.0
  • Modify composer.json to drop support for PHP less than 7.3
  • Modify composer.json to implement phpunit 9.3 which supports PHP 7.3+
  • Modify .travis.yml to ignore platform requirements when installing composer dependencies (simply add --ignore-platform-reqs to COMPOSER_ARGS env variable)
  • Modify .travis.yml to add PHP 8.0 to the matrix (NOTE: Do not allow failures as PHP 8.0 has a feature freeze since 2020-08-04!)
  • Modify source code in case there are incompatibilities with PHP 8.0

Adapter example in README.md is wrong

Bug Report

Q A
Version(s) 2.11.2

Summary

laminas-cache-adapter-storage-memory is mentioned in the example in README.md, but it should rather be laminas-cache-storage-adapter-memory.

Current behavior

Mention of laminas-cache-adapter-storage-memory.

Expected behavior

Mention of laminas-cache-storage-adapter-memory.

I got this error with cron job after few minutes

Bug Report

Q A
Version(s) ^2.8

Summary

Uncaught Exception Error: Class 'Laminas\Cache\Psr\SimpleCache\SimpleCacheException' not found in /var/www/html/glpi/vendor/laminas/laminas-cache/src/Psr/SimpleCache/SimpleCacheDecorator.php at line 307

Current behavior

How to reproduce

Expected behavior

[RFC]: Move `StorageInterface::getMetadata` to `MetadataCapableInterface`

RFC

Q A
Proposed Version(s) 4.00
BC Break? Yes

Goal

Having those adapters which do support metadata must explicitly implement that interface rather than assuming that all adapters will have that capability.

Background

Just a handful of adapters do support Metadata. Almost all adapters have different metadatas aswell. With having a dedicated interface which also have a template to annotate the generic Metadata instance with getters of these metadatas will provide properly typed informations long-term.

Given the example of the filesystem adapter which can provided filesystem-specific metadatas (mtime, atime, ...), almost all metadatas are already "explicitly" used. So upstream projects will probably implement Filesystem directly rather than using StorageInterface.
If they do, there might be a point where they use assert($storage instanceOf Filesystem); because otherwise, that wont make any sense when using getMetadata while also accessing the atime array key.

With having that new interface in addition with the generic annotation to mark the Metadata instance, we can provide a FilesystemMetadata instance which then provides type-safe getters for mtime and atime.

So in the end, all adapters have to provide their own metadata implementation.

Considerations

As already mentioned, I assume that those users which do make use of the StorageInterface::getMetadata do this, because they know the underlying storage implementation.
Currently usage would be something like:

$storage = new Filesystem();

$metadata = $storage->getMetadata('key1');
assert($metadata !== false);

$cacheCreatedOn = $metadata['atime'];

This would never work with the Redis or Memcached implementation. So in the end, we will provide proper FilesystemMetadata instance with properly typed methods to provide metadata informations of existing cache keys. Migration to the new functionality would also quite easy because instead of accessing array keys, users only have to access the provided getters/setters.

Proposal(s)

The MetadataCapableInterface would have been asserted by either using instanceof or by directly consuming the adapter rather than the interface.
I guess, thats already the case in those projects which do use the StorageInterface::getMedata

Appendix

The new MetadataCapableInterface might look like this:

/**
 * @template TMetadata of object
 */
interface MetadataCapableInterface
{
	/**
	 * @psalm-return TMetadata
	 */
	public function getMetadata(string $key): ?object;
}

A possible implementation in the Filesystem adapter could look like this:

final class FilesystemMetadata
{
	private $mtime;
	private $atime;
	// [...]

    public function __construct(int $mtime, int $atime)
    {}

    public function getMTime(): int
    {}

    public function getATime(): int
    {}
}
/**
 * @template-implements MetadataCapableInterface<FilesystemMetadata>
 */
final class Filesystem extends AbstractAdapter implements MetadataCapableInterface
{
	public function getMetadata(string $key): ?object
	{
		if (!$this->hasItem($key)) {
			return null;
		}

		$options  = $this->getOptions();
        $filespec = $this->getFileSpec($key);
        $file     = $this->formatFilename($filespec);

        return new FilesystemMetadata(filemtime($file), fileatime($file));
	}
}

Currently available metadata (depending on adapters)

these links might be outdated in the future as we dont have adapter-specific documentations available and all adapters share the same configuration structure. @froschdesign maybe finds a way to have this fixed in future documentation implementations (as stated in one of my other PRs).

https://docs.laminas.dev/laminas-cache/storage/adapter/#capabilities
https://docs.laminas.dev/laminas-cache/storage/adapter/#capabilities_3
https://docs.laminas.dev/laminas-cache/storage/adapter/#capabilities_5
https://docs.laminas.dev/laminas-cache/storage/adapter/#capabilities_6
https://docs.laminas.dev/laminas-cache/storage/adapter/#capabilities_7
https://docs.laminas.dev/laminas-cache/storage/adapter/#capabilities_8
https://docs.laminas.dev/laminas-cache/storage/adapter/#capabilities_9
https://docs.laminas.dev/laminas-cache/storage/adapter/#capabilities_10
https://docs.laminas.dev/laminas-cache/storage/adapter/#capabilities_11

PHP 8.0 support for v2

Hi, since we need all satellite packages to be ready before releasing PHP 8 support for laminas-cache v2, here's the packages involved to ease track of progress:

Maintained

Abandoned

  • laminas-cache-storage-adapter-apc
  • laminas-cache-storage-adapter-dba
  • laminas-cache-storage-adapter-mongodb
  • laminas-cache-storage-adapter-wincache
  • laminas-cache-storage-adapter-xcache
  • laminas-cache-storage-adapter-zend-server

Should a raw string be serialized in Redis when using the SimpleCacheDecorator?

Bug Report

Q A
Version(s) 2.10.0 (But possible in other versions too)

Summary

I'm trying to insert a raw JSON string into my cache but I'm experiencing this error:

The storage adapter "Laminas\Cache\Storage\Adapter\Redis" requires a serializer plugin; please see https://docs.laminas.dev/laminas-cache/storage/plugin/#quick-start for details on how to attach the plugin to your adapter."

This error is thrown in the Laminas\Cache\Psr\SimpleCache\SimpleCacheDecorator class which checks whether serialization is required or not. In my case I just want to insert a raw string which doesn't require any serialization. Is this expected behavior?

Current behavior

An error is thrown when sending a string to my cache. Please refer to the summary above.

How to reproduce

Send a string to a Redis cache decorated with the SimpleCacheDecorator without the serializer plugin enabled.

Expected behavior

I'm not really sure, but I think it should just insert the string in Redis?

Require `mbstring` with v3

Feature Request

Q A
New Feature yes
BC Break yes

Summary

As this library supports PSR-6 and PSR-16, it also needs ways to handle multibyte strings which are allowed as per these recommendations.
To fix #154 properly, a multibyte check needs to be done when a key exceeds a certain limit.
This is actually only the case for the redis adapter, so until v3, the redis adapter will decrease its maximum key length to 65534 as 65535 is the maximum number to be used as a specifier for pcre2.

https://3v4l.org/GURWn

`Serializer` plugin does not store the initial values when incrementing/decrementing values

Bug Report

Q A
Version(s) 2.11.1

Summary

Given the AbstractCommonAdapterTest::testIncrementItemInitialValue and AbstractCommonAdapterTest::testDecrementItemInitialValue, the adapter has to treat non-existent values as 0 when using increment or decrement methods.

Current behavior

When using the Serializer plugin, increment and decrement will immediately stop due to the plugin stopping the propagation of the event.

How to reproduce

There are existing unit tests which are already failing within the AbstractCommonAdapterTest.

public function testDecrementItemInitialValue(): void
{
    $this->assertEquals(-5, $this->storage->decrementItem('counter', 5));
    $this->assertEquals(-5, $this->storage->getItem('counter'));
}
public function testIncrementItemInitialValue(): void
{
    $this->assertEquals(5, $this->storage->incrementItem('counter', 5));
    $this->assertEquals(5, $this->storage->getItem('counter'));
}

Both tests have to be executed with adapters implementing the Serializer plugin.

Expected behavior

Tests are passing.

[RFC]: Removal of increment/decrement functionality

RFC

Q A
Proposed Version(s) 4.0.0
BC Break? Yes

Goal

It seems that some storage backends (redis for example) does not preserve type-safety when storing values.
Incrementing and decrementing of cache items is therefore mostly done by this library by reading the value, Incrementing/decrementing the value and then it is written back to the storage.
This could also be done in userland code and/or by a decorator and thus does not need a requirement for a storage anymore.

Background

Recently, there were 2 bugs discovered when using the serializer plugin in combination with decrement/increment which were undetected for around 9 years.

Considerations

Providing a decorator which consumes the storage along with the key & amount could provide the same functionality without having every storage do handle it by itself.

Proposal(s)

Thus said, I would mark that whole functionality deprecated in 3.0 and remove it in 4.0

Decorator to increment/decrement could be created in 3.0 if needed. The whole functionality could be done in userland without having to use a decorator at all and thus the decorator could also be just an example in the docs.

Appendix

Bugs mentioned in this RFC which are related to this functionality:
#116
#115

SimpleCacheDecorator and providesPerItemTtl leads undocumented behaviors

The SimpleCacheDecorator returns false and doesn't store the item if the underlying storage doesn't has the staticTtl capability.
For testing purposes I changed from an APCU storage to memory storage. Our tests where failing due to the fact that the memory storage has no per item ttl support => in other words staticTtl = false. https://github.com/zendframework/zend-cache/blob/580cb67bf645c1765c3463b16c97903d797c3b19/src/Psr/SimpleCache/SimpleCacheDecorator.php#L360

In our code base we where not checking the return value of CacheInterface::set(), which in the case of memory return false.

<?php
use Zend\Cache\StorageFactory;
use Zend\Cache\Psr\SimpleCache\SimpleCacheDecorator;

$storage = StorageFactory::factory([
    'adapter' => [
        'name'    => 'memory',
        'options' => ['ttl' => 3600],
    ],
]);

$cache = new SimpleCacheDecorator($storage);

if (true === $cache->set('someKey', $value, 30)) {
    echo 'success';
} else {
    echo 'fail';
}

The documentation has no information about this behavior:

When setting values, whether single values or multiple, you can also optionally provide a Time To Live (TTL) value. This proxies to the underlying storage instance's options, temporarily resetting its TTL value for the duration of the operation. TTL values may be expressed as integers (in which case they represent seconds) or DateInterval instances. As examples:

In our case we set a TTL on the adapter level. But we also set the TTL per item to make sure the item has the same TTL even when we replace the underlying PSR-16 library.

Our workaround for the moment is to set the per item TTL to null for an Zend cache adapter that doesn't support staticTtl. This adds some unwanted conditional if/else and knowledge about the special behavior. There is also the problem if the internal detection state of providesPerItemTtl changes in never releases we need to adopt to this changes.

Possible solution (eater one of them or a combination)##

  • If the TTL of the item is the same as the TTL of the storage adapter allow the per item TTL.
  • If no storage adapter TTL is set silently ignore the TTL and fall back to the storage default => this is what https://www.php-fig.org/psr/psr-16/#13-cache suggests
  • Add a configuration flag to the SimpleCacheDecorator to handle the failure state:
    • ignore failure of storage not supports per item ttl => this helps getting aware of the issue and can be added to the docs
  • expose the storage and providesPerItemTtl to allow for workarounds

Originally posted by @dol at zendframework/zend-cache#171

Overall documentation improvements

The versioning is only the first step, the structure also needs an update. At the moment I see the following problems:

  • introduction is missing with descriptions for storage, patterns, etc.
  • at the moment, the basic usage page is not included in the navigation
  • available adapters are hidden
  • the adapters page is too long

My suggestions:

  • create an introduction
  • rework the entire quick start page and add it to the navigation
  • create a separate page for each adapter, then they can be listed in the navigation
  • add descriptions for application integrations (laminas-mvc, Mezzio)
  • extend the homepage and add the features of laminas-cache (available adapters, PSR support, etc.)

Originally posted by @froschdesign in #136 (comment)

PSR-6 `CacheItemPoolDecorator` does not validate the maximum cache key length

Feature Request

Q A
New Feature yes
BC Break yes

Summary

As per PSR-6, a minimum cache key length of 64 characters have to be supported by the underlying storage.
The maximum can vary and depends on the implementing library.

For PSR-16 there was already a bugfix which allowed cache keys longer than 64 characters but for the CacheItemPoolDecorator, there is no such validation.

reduce arguments by reference

This PR reduces arguments by reference often used on internal methods.

It was previously done as performance improvement but in fact it decreases performance as PHP have to create a new zval and in some cases it needs to copy the value before PHP-7 as described here: http://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.html

Additionally arguments by reference make the code more error prone and hard to read.


Originally posted by @marc-mabe at zendframework/zend-cache#8

Cleanup capabilities

Some of the current defined capabilities are very hard to understand/useless and I would like to define some of them.

minTtl
This defines the minimum supported TTL which is clear but only a handful people knows that this also defines the general TTL support.

  • 0 means TTL is supported

  • 0 means TTL is not supported and all other TTL related capabilities can be ignored

There is no adapter having another value than 0 or 1 so this can be renamed to ttlSupported with bool.

-> minTtl needs to be still available for BC reasons until the next major but can be marked deprecated.

staticTtl
This defines how the expiration time will be calculated which is not a meaningful description.

  • true means the expiration time will be calculated on read using the last-modification-time of the item, the current time and the current TTL option
  • false means the expiration time will be calculated using the current TTL and the current time. It will be stored together with the item

Thoughts for better names ?

expiredRead
There was the idea that it is possible to read an expired item by setting changing the TTL (like to 0 = infinitive) but this only works with staticTtl=true to make it possible using an expired item in cases regeneration breaks (like on failed DB connection).

This is a useless capability because changing the TTL to something else automatically changes the expiration time of all stored items if staticTtl=true which means in case for infinity the item is no longer expired. -> Bam the item is no longer expired but this capability is for reading expired items and the nature of staticTtl already defines this behavior.

-> I would like to deprecate this capability and remove it in the next major version.

@kynx @Maks3w @Ocramius @ezimuel Thoughts ?


Originally posted by @marc-mabe at zendframework/zend-cache#81

Upstream cache pattern may use `InvokableFactory` which will result in `TypeError`

Bug Report

Q A
Version(s) 2.12.0

Summary

In 2.12.0, a AbstractPattern#__construct was added which is consuming the PatternOptions.
In case a project is using the InvokableFactory, this may break upstream projects.

NOTE
This is only a BC break when a project created an own cache pattern which extends the AbstractPattern and consumes the PatternOptions at the same time.

Current behavior

When InvokableFactory is used with a cache pattern which consumes options, a TypeError is raised due to type-incompatibility with AbstractPattern#__construct

How to reproduce

new \Laminas\Cache\Pattern\CaptureCache(['umask');

Expected behavior

No TypeError.

Make it possible to decorate cache adapters with PSR6/PSR16 decorators through configuration

@weierophinney

Good morning,

The configuration provider currently provides the \Zend\Cache\Service\StorageCacheAbstractServiceFactory::class abstract factory which make it possible to map pseudo services. For instance:

// Dependencies
...
'factories' => [
	\iMPSCP\ApplicationCache::class => \Zend\Cache\Service\StorageCacheAbstractServiceFactory::class
]
...

// Config
...
 'caches' => [
	\iMSCP\ApplicationCache::class => [
		'adapter' => [
			'name'    => \Zend\Cache\Storage\Adapter\Apcu::class,
			'options' => [
				'namespace' => \iMSCP\ApplicationCache::class,
			]
		],
		'plugins' => [
			\Zend\Cache\Storage\Plugin\ExceptionHandler::class => [
				'throw_exceptions' => false,
			],
			\Zend\Cache\Storage\Plugin\IgnoreUserAbort::class => [
				'exit_on_abort' => false
			]
		]
	]
]
...

However, currently, it seem that there is no way to decorate the adapters automatically with a PSR6 or PSR16 implementation. Could it be possible to add a decorator option to the adapters factory and if so, automatically return the decorated adapter? For instance:

// Config
...
 'caches' => [
	\iMSCP\ApplicationCache::class => [
		'adapter' => [
			'name'    => \Zend\Cache\Storage\Adapter\Apcu::class,
			'options' => [
				'namespace' => \iMSCP\ApplicationCache::class,
			]
		],
		'plugins' => [
			\Zend\Cache\Storage\Plugin\ExceptionHandler::class => [
				'throw_exceptions' => false,
			],
			\Zend\Cache\Storage\Plugin\IgnoreUserAbort::class => [
				'exit_on_abort' => false
			]
		],
		decorator' => \Zend\Cache\Psr\SimpleCache\SimpleCacheDecorator::class
	]
]
...

For now, I make use of a delegator but...

<?php

declare(strict_types=1);

namespace iMSCP\Foundation\Container;

use Psr\Container\ContainerInterface;
use Psr\SimpleCache\CacheInterface;
use Zend\Cache\Psr\SimpleCache\SimpleCacheDecorator;
use Zend\Cache\Storage\StorageInterface;

class ApplicationCacheDelegatorFactory
{
    /**
     * Decorate a cache adapter with a PSR16 implementation
     *
     * @param ContainerInterface $container
     * @param string $serviceName
     * @param callable $callback
     * @return CacheInterface
     */
    public function __invoke(ContainerInterface $container, string $serviceName, callable $callback): CacheInterface
    {
        try {
            /** @var StorageInterface $storage */
            $storage = $callback();
            
            if($container->get('config')['debug'] ?? false) {
                $storage->setOptions([
                    'readable'  => false,
                    'writable'  => false,
                ]);
            }
            
            // PSR-16 implementation
            $storage = new SimpleCacheDecorator($storage);
        } catch (\Throwable $e) {
            // PSR-16 implementation (fallback)
            $storage = $this->fallbackPsr16Impl();
        }

        return $storage;
    }

    protected function fallbackPsr16Impl()
    {
        return (new class implements CacheInterface
        {
            public function get($key, $default = NULL)
            {
                return false;
            }

            public function set($key, $value, $ttl = NULL)
            {
                return false;
            }

            public function delete($key)
            {
                return false;
            }

            public function clear()
            {
                return false;
            }

            public function getMultiple($keys, $default = NULL)
            {
                return [];
            }

            public function setMultiple($values, $ttl = NULL)
            {
                return false;
            }
            
            public function deleteMultiple($keys)
            {
                return false;
            }

            public function has($key)
            {
                return false;
            }
        });
    }
}

Originally posted by @nuxwin at zendframework/zend-cache#179

Removal of abandoned storage adapter from documentation

Feature Request

Q A
Documentation yes

Summary

With the Technical Steering Committee Meeting in May 2021, we decided to abandon some cache adapters for several reasons.

Components are already archived and thus wont receive any new releases.

The only thing what is missing: Documentation removal of these components.

We can do this with v3 while keeping documentation for v2. This would require us to provide versionized documentation for this component which is actually unavailable. I think I need guidance by @froschdesign or @weierophinney to make that happen.

Psalm integration

Feature Request

Q A
QA yes

Summary

As decided during the Technical-Steering-Committee Meeting on August 3rd, 2020, Laminas wants to implement vimeo/psalm in all packages.

Implementing psalm is quite easy.

Required

  • Create a psalm.xml in the project root
  • Copy and paste the contents from this psalm.xml.dist
  • Run $ composer require --dev vimeo/psalm
  • Run $ vendor/bin/psalm --set-baseline=psalm-baseline.xml
  • Add a composer script static-analysis with the command psalm --shepherd --stats
  • Add a new line to script: in .travis.yml: - if [[ $TEST_COVERAGE == 'true' ]]; then composer static-analysis ; fi
  • Remove phpstan from the project (phpstan.neon.dist, .travis.yml entry, composer.json require-dev and scripts)
Optional
  • Fix as many psalm errors as possible.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.