bug #32025 SimpleCacheAdapter fails to cache any item if a namespace is used (moufmouf)
This PR was squashed before being merged into the 3.4 branch (closes #32025).
Discussion
----------
SimpleCacheAdapter fails to cache any item if a namespace is used
| Q | A
| ------------- | ---
| Branch? |3.4
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | no
| Tests pass? | yes
| License | MIT
This is a backport of #32019
The SimpleCacheAdapter extends AdapterTestCase.
When adding a namespace, the AdapterTestCase adds ":" after the namespace:
https://github.com/symfony/symfony/blob/v4.3.1/src/Symfony/Component/Cache/Adapter/AbstractAdapter.php#L37
The namespace is prepended to the cache key.
But in PSR-16, the ":" is a forbidden character.
As a result, the cache key is invalid and cache is not persisted. If you use Psr16Adapter + a namespace, the cache simply does not work.
As per @nicolas-grekas advices, a NS_SEPARATOR const is added to change the namespace separator for the `SimpleCacheAdapter` to "_" (that is compatible with PSR-16).
The first commit of this PR starts with an additional test and no fix (to showcase the problem).
Commits
-------
ffd3469ddf
SimpleCacheAdapter fails to cache any item if a namespace is used
This commit is contained in:
commit
cfbb5b50b1
@ -39,7 +39,7 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
|
|||||||
*/
|
*/
|
||||||
protected function __construct($namespace = '', $defaultLifetime = 0)
|
protected function __construct($namespace = '', $defaultLifetime = 0)
|
||||||
{
|
{
|
||||||
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':';
|
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).static::getNsSeparator();
|
||||||
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
|
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
|
||||||
throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, \strlen($namespace), $namespace));
|
throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, \strlen($namespace), $namespace));
|
||||||
}
|
}
|
||||||
|
@ -75,4 +75,12 @@ class SimpleCacheAdapter extends AbstractAdapter implements PruneableInterface
|
|||||||
{
|
{
|
||||||
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
|
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the namespace separator for cache keys
|
||||||
|
*/
|
||||||
|
protected static function getNsSeparator()
|
||||||
|
{
|
||||||
|
return '_';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Cache\Tests\Adapter;
|
namespace Symfony\Component\Cache\Tests\Adapter;
|
||||||
|
|
||||||
use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
|
use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
|
||||||
|
use Symfony\Component\Cache\Simple\ArrayCache;
|
||||||
use Symfony\Component\Cache\Simple\FilesystemCache;
|
use Symfony\Component\Cache\Simple\FilesystemCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,4 +28,14 @@ class SimpleCacheAdapterTest extends AdapterTestCase
|
|||||||
{
|
{
|
||||||
return new SimpleCacheAdapter(new FilesystemCache(), '', $defaultLifetime);
|
return new SimpleCacheAdapter(new FilesystemCache(), '', $defaultLifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testValidCacheKeyWithNamespace()
|
||||||
|
{
|
||||||
|
$cache = new SimpleCacheAdapter(new ArrayCache(), 'some_namespace', 0);
|
||||||
|
$item = $cache->getItem('my_key');
|
||||||
|
$item->set('someValue');
|
||||||
|
$cache->save($item);
|
||||||
|
|
||||||
|
$this->assertTrue($cache->getItem('my_key')->isHit(), 'Stored item is successfully retrieved.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,7 +106,7 @@ trait AbstractTrait
|
|||||||
{
|
{
|
||||||
$this->deferred = [];
|
$this->deferred = [];
|
||||||
if ($cleared = $this->versioningIsEnabled) {
|
if ($cleared = $this->versioningIsEnabled) {
|
||||||
$namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), ':', 5);
|
$namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::getNsSeparator(), 5);
|
||||||
try {
|
try {
|
||||||
$cleared = $this->doSave(['@'.$this->namespace => $namespaceVersion], 0);
|
$cleared = $this->doSave(['@'.$this->namespace => $namespaceVersion], 0);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
@ -235,13 +235,13 @@ trait AbstractTrait
|
|||||||
CacheItem::validateKey($key);
|
CacheItem::validateKey($key);
|
||||||
|
|
||||||
if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
|
if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
|
||||||
$this->namespaceVersion = '1:';
|
$this->namespaceVersion = '1'.static::getNsSeparator();
|
||||||
try {
|
try {
|
||||||
foreach ($this->doFetch(['@'.$this->namespace]) as $v) {
|
foreach ($this->doFetch(['@'.$this->namespace]) as $v) {
|
||||||
$this->namespaceVersion = $v;
|
$this->namespaceVersion = $v;
|
||||||
}
|
}
|
||||||
if ('1:' === $this->namespaceVersion) {
|
if ('1'.static::getNsSeparator() === $this->namespaceVersion) {
|
||||||
$this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), ':', 5);
|
$this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), static::getNsSeparator(), 5);
|
||||||
$this->doSave(['@'.$this->namespace => $this->namespaceVersion], 0);
|
$this->doSave(['@'.$this->namespace => $this->namespaceVersion], 0);
|
||||||
}
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
@ -252,7 +252,7 @@ trait AbstractTrait
|
|||||||
return $this->namespace.$this->namespaceVersion.$key;
|
return $this->namespace.$this->namespaceVersion.$key;
|
||||||
}
|
}
|
||||||
if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
|
if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
|
||||||
$id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), ':', -(\strlen($this->namespaceVersion) + 22));
|
$id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), static::getNsSeparator(), -(\strlen($this->namespaceVersion) + 22));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $id;
|
return $id;
|
||||||
@ -265,4 +265,12 @@ trait AbstractTrait
|
|||||||
{
|
{
|
||||||
throw new \DomainException('Class not found: '.$class);
|
throw new \DomainException('Class not found: '.$class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string the namespace separator for cache keys
|
||||||
|
*/
|
||||||
|
protected static function getNsSeparator()
|
||||||
|
{
|
||||||
|
return ':';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user