Skip to content

Commit cfbb5b5

Browse files
committed
bug symfony#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 symfony#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 symfony#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 ------- ffd3469 SimpleCacheAdapter fails to cache any item if a namespace is used
2 parents 575e922 + ffd3469 commit cfbb5b5

File tree

4 files changed

+33
-6
lines changed

4 files changed

+33
-6
lines changed

src/Symfony/Component/Cache/Adapter/AbstractAdapter.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ abstract class AbstractAdapter implements AdapterInterface, LoggerAwareInterface
3939
*/
4040
protected function __construct($namespace = '', $defaultLifetime = 0)
4141
{
42-
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).':';
42+
$this->namespace = '' === $namespace ? '' : CacheItem::validateKey($namespace).static::getNsSeparator();
4343
if (null !== $this->maxIdLength && \strlen($namespace) > $this->maxIdLength - 24) {
4444
throw new InvalidArgumentException(sprintf('Namespace must be %d chars max, %d given ("%s")', $this->maxIdLength - 24, \strlen($namespace), $namespace));
4545
}

src/Symfony/Component/Cache/Adapter/SimpleCacheAdapter.php

+8
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,12 @@ protected function doSave(array $values, $lifetime)
7575
{
7676
return $this->pool->setMultiple($values, 0 === $lifetime ? null : $lifetime);
7777
}
78+
79+
/**
80+
* @return string the namespace separator for cache keys
81+
*/
82+
protected static function getNsSeparator()
83+
{
84+
return '_';
85+
}
7886
}

src/Symfony/Component/Cache/Tests/Adapter/SimpleCacheAdapterTest.php

+11
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\Cache\Tests\Adapter;
1313

1414
use Symfony\Component\Cache\Adapter\SimpleCacheAdapter;
15+
use Symfony\Component\Cache\Simple\ArrayCache;
1516
use Symfony\Component\Cache\Simple\FilesystemCache;
1617

1718
/**
@@ -27,4 +28,14 @@ public function createCachePool($defaultLifetime = 0)
2728
{
2829
return new SimpleCacheAdapter(new FilesystemCache(), '', $defaultLifetime);
2930
}
31+
32+
public function testValidCacheKeyWithNamespace()
33+
{
34+
$cache = new SimpleCacheAdapter(new ArrayCache(), 'some_namespace', 0);
35+
$item = $cache->getItem('my_key');
36+
$item->set('someValue');
37+
$cache->save($item);
38+
39+
$this->assertTrue($cache->getItem('my_key')->isHit(), 'Stored item is successfully retrieved.');
40+
}
3041
}

src/Symfony/Component/Cache/Traits/AbstractTrait.php

+13-5
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public function clear()
106106
{
107107
$this->deferred = [];
108108
if ($cleared = $this->versioningIsEnabled) {
109-
$namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), ':', 5);
109+
$namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::getNsSeparator(), 5);
110110
try {
111111
$cleared = $this->doSave(['@'.$this->namespace => $namespaceVersion], 0);
112112
} catch (\Exception $e) {
@@ -235,13 +235,13 @@ private function getId($key)
235235
CacheItem::validateKey($key);
236236

237237
if ($this->versioningIsEnabled && '' === $this->namespaceVersion) {
238-
$this->namespaceVersion = '1:';
238+
$this->namespaceVersion = '1'.static::getNsSeparator();
239239
try {
240240
foreach ($this->doFetch(['@'.$this->namespace]) as $v) {
241241
$this->namespaceVersion = $v;
242242
}
243-
if ('1:' === $this->namespaceVersion) {
244-
$this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), ':', 5);
243+
if ('1'.static::getNsSeparator() === $this->namespaceVersion) {
244+
$this->namespaceVersion = substr_replace(base64_encode(pack('V', time())), static::getNsSeparator(), 5);
245245
$this->doSave(['@'.$this->namespace => $this->namespaceVersion], 0);
246246
}
247247
} catch (\Exception $e) {
@@ -252,7 +252,7 @@ private function getId($key)
252252
return $this->namespace.$this->namespaceVersion.$key;
253253
}
254254
if (\strlen($id = $this->namespace.$this->namespaceVersion.$key) > $this->maxIdLength) {
255-
$id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), ':', -(\strlen($this->namespaceVersion) + 22));
255+
$id = $this->namespace.$this->namespaceVersion.substr_replace(base64_encode(hash('sha256', $key, true)), static::getNsSeparator(), -(\strlen($this->namespaceVersion) + 22));
256256
}
257257

258258
return $id;
@@ -265,4 +265,12 @@ public static function handleUnserializeCallback($class)
265265
{
266266
throw new \DomainException('Class not found: '.$class);
267267
}
268+
269+
/**
270+
* @return string the namespace separator for cache keys
271+
*/
272+
protected static function getNsSeparator()
273+
{
274+
return ':';
275+
}
268276
}

0 commit comments

Comments
 (0)