Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 14614bd

Browse filesBrowse files
committed
feature #32284 [Cache] Add argument $prefix to AdapterInterface::clear() (nicolas-grekas)
This PR was merged into the 4.4 branch. Discussion ---------- [Cache] Add argument $prefix to AdapterInterface::clear() | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | - | License | MIT | Doc PR | - This PR allows clearing `AdapterInterface` implementations by prefix of keys. The goal is to fix an edge case situation in `ProxyAdapter`: right now, when one calls `->clear()` on a proxy adapter that is configured to add a prefix to all the keys passed to the decorated pool, we clear it all; while only the subset that starts with the prefix should be. Since `AdapterInterface` is an "internal" interface (ie its purpose is to create compatible implementations - *NOT* to be type-hinted for), this is not really a user-facing change. /cc @Nyholm, this came out after we talked about proxified chain pools Commits ------- ad6f6cf [Cache] Add argument $prefix to AdapterInterface::clear()
2 parents 3e2ee71 + ad6f6cf commit 14614bd
Copy full SHA for 14614bd
Expand file treeCollapse file tree

21 files changed

+153
-16
lines changed

‎UPGRADE-4.4.md

Copy file name to clipboardExpand all lines: UPGRADE-4.4.md
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
UPGRADE FROM 4.3 to 4.4
22
=======================
33

4+
Cache
5+
-----
6+
7+
* Added argument `$prefix` to `AdapterInterface::clear()`
8+
49
DependencyInjection
510
-------------------
611

‎UPGRADE-5.0.md

Copy file name to clipboardExpand all lines: UPGRADE-5.0.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Cache
1616
* Removed `CacheItem::getPreviousTags()`, use `CacheItem::getMetadata()` instead.
1717
* Removed all PSR-16 adapters, use `Psr16Cache` or `Symfony\Contracts\Cache\CacheInterface` implementations instead.
1818
* Removed `SimpleCacheAdapter`, use `Psr16Adapter` instead.
19+
* Added argument `$prefix` to `AdapterInterface::clear()`
1920

2021
Config
2122
------

‎src/Symfony/Component/Cache/Adapter/AdapterInterface.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/AdapterInterface.php
+7Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,11 @@ public function getItem($key);
3434
* @return \Traversable|CacheItem[]
3535
*/
3636
public function getItems(array $keys = []);
37+
38+
/**
39+
* {@inheritdoc}
40+
*
41+
* @param string $prefix
42+
*/
43+
public function clear(/*string $prefix = ''*/);
3744
}

‎src/Symfony/Component/Cache/Adapter/ChainAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/ChainAdapter.php
+9-2Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,14 +192,21 @@ public function hasItem($key)
192192

193193
/**
194194
* {@inheritdoc}
195+
*
196+
* @param string $prefix
195197
*/
196-
public function clear()
198+
public function clear(/*string $prefix = ''*/)
197199
{
200+
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
198201
$cleared = true;
199202
$i = $this->adapterCount;
200203

201204
while ($i--) {
202-
$cleared = $this->adapters[$i]->clear() && $cleared;
205+
if ($this->adapters[$i] instanceof AdapterInterface) {
206+
$cleared = $this->adapters[$i]->clear($prefix) && $cleared;
207+
} else {
208+
$cleared = $this->adapters[$i]->clear() && $cleared;
209+
}
203210
}
204211

205212
return $cleared;

‎src/Symfony/Component/Cache/Adapter/NullAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/NullAdapter.php
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ public function hasItem($key)
7575

7676
/**
7777
* {@inheritdoc}
78+
*
79+
* @param string $prefix
7880
*/
79-
public function clear()
81+
public function clear(/*string $prefix = ''*/)
8082
{
8183
return true;
8284
}

‎src/Symfony/Component/Cache/Adapter/ProxyAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/ProxyAdapter.php
+9-1Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,17 @@ public function hasItem($key)
147147

148148
/**
149149
* {@inheritdoc}
150+
*
151+
* @param string $prefix
150152
*/
151-
public function clear()
153+
public function clear(/*string $prefix = ''*/)
152154
{
155+
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
156+
157+
if ($this->pool instanceof AdapterInterface) {
158+
return $this->pool->clear($this->namespace.$prefix);
159+
}
160+
153161
return $this->pool->clear();
154162
}
155163

‎src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/TagAwareAdapter.php
+18-2Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -213,10 +213,26 @@ public function getItems(array $keys = [])
213213

214214
/**
215215
* {@inheritdoc}
216+
*
217+
* @param string $prefix
216218
*/
217-
public function clear()
219+
public function clear(/*string $prefix = ''*/)
218220
{
219-
$this->deferred = [];
221+
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
222+
223+
if ('' !== $prefix) {
224+
foreach ($this->deferred as $key => $item) {
225+
if (0 === strpos($key, $prefix)) {
226+
unset($this->deferred[$key]);
227+
}
228+
}
229+
} else {
230+
$this->deferred = [];
231+
}
232+
233+
if ($this->pool instanceof AdapterInterface) {
234+
return $this->pool->clear($prefix);
235+
}
220236

221237
return $this->pool->clear();
222238
}

‎src/Symfony/Component/Cache/Adapter/TraceableAdapter.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Adapter/TraceableAdapter.php
+8-1Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,18 @@ public function getItems(array $keys = [])
167167

168168
/**
169169
* {@inheritdoc}
170+
*
171+
* @param string $prefix
170172
*/
171-
public function clear()
173+
public function clear(/*string $prefix = ''*/)
172174
{
175+
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
173176
$event = $this->start(__FUNCTION__);
174177
try {
178+
if ($this->pool instanceof AdapterInterface) {
179+
return $event->result = $this->pool->clear($prefix);
180+
}
181+
175182
return $event->result = $this->pool->clear();
176183
} finally {
177184
$event->end = microtime(true);

‎src/Symfony/Component/Cache/CHANGELOG.md

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ CHANGELOG
55
-----
66

77
* added support for connecting to Redis Sentinel clusters
8+
* added argument `$prefix` to `AdapterInterface::clear()`
89

910
4.3.0
1011
-----

‎src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Tests/Adapter/AdapterTestCase.php
+20Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,26 @@ public function testPrune()
252252
$this->assertFalse($this->isPruned($cache, 'foo'));
253253
$this->assertTrue($this->isPruned($cache, 'qux'));
254254
}
255+
256+
public function testClearPrefix()
257+
{
258+
if (isset($this->skippedTests[__FUNCTION__])) {
259+
$this->markTestSkipped($this->skippedTests[__FUNCTION__]);
260+
}
261+
262+
$cache = $this->createCachePool(0, __FUNCTION__);
263+
$cache->clear();
264+
265+
$item = $cache->getItem('foobar');
266+
$cache->save($item->set(1));
267+
268+
$item = $cache->getItem('barfoo');
269+
$cache->save($item->set(2));
270+
271+
$cache->clear('foo');
272+
$this->assertFalse($cache->hasItem('foobar'));
273+
$this->assertTrue($cache->hasItem('barfoo'));
274+
}
255275
}
256276

257277
class NotUnserializable

‎src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Tests/Adapter/DoctrineAdapterTest.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class DoctrineAdapterTest extends AdapterTestCase
2323
'testDeferredSaveWithoutCommit' => 'Assumes a shared cache which ArrayCache is not.',
2424
'testSaveWithoutExpire' => 'Assumes a shared cache which ArrayCache is not.',
2525
'testNotUnserializable' => 'ArrayCache does not use serialize/unserialize',
26+
'testClearPrefix' => 'Doctrine cannot clear by prefix',
2627
];
2728

2829
public function createCachePool($defaultLifetime = 0)

‎src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Tests/Adapter/MemcachedAdapterTest.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class MemcachedAdapterTest extends AdapterTestCase
1919
protected $skippedTests = [
2020
'testHasItemReturnsFalseWhenDeferredItemIsExpired' => 'Testing expiration slows down the test suite',
2121
'testDefaultLifeTime' => 'Testing expiration slows down the test suite',
22+
'testClearPrefix' => 'Memcached cannot clear by prefix',
2223
];
2324

2425
protected static $client;

‎src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Tests/Adapter/PhpArrayAdapterTest.php
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ protected function tearDown()
7272

7373
public function createCachePool($defaultLifetime = 0, $testMethod = null)
7474
{
75-
if ('testGetMetadata' === $testMethod) {
75+
if ('testGetMetadata' === $testMethod || 'testClearPrefix' === $testMethod) {
7676
return new PhpArrayAdapter(self::$file, new FilesystemAdapter());
7777
}
7878

‎src/Symfony/Component/Cache/Tests/Adapter/Psr16AdapterTest.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Tests/Adapter/Psr16AdapterTest.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class Psr16AdapterTest extends AdapterTestCase
2323
{
2424
protected $skippedTests = [
2525
'testPrune' => 'Psr16adapter just proxies',
26+
'testClearPrefix' => 'SimpleCache cannot clear by prefix',
2627
];
2728

2829
public function createCachePool($defaultLifetime = 0)

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Tests/Adapter/SimpleCacheAdapterTest.php
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ class SimpleCacheAdapterTest extends AdapterTestCase
2323
{
2424
protected $skippedTests = [
2525
'testPrune' => 'SimpleCache just proxies',
26+
'testClearPrefix' => 'SimpleCache cannot clear by prefix',
2627
];
2728

2829
public function createCachePool($defaultLifetime = 0)

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

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/AbstractTrait.php
+5-2Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,12 @@ public function hasItem($key)
102102

103103
/**
104104
* {@inheritdoc}
105+
*
106+
* @param string $prefix
105107
*/
106-
public function clear()
108+
public function clear(/*string $prefix = ''*/)
107109
{
110+
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
108111
$this->deferred = [];
109112
if ($cleared = $this->versioningIsEnabled) {
110113
$namespaceVersion = substr_replace(base64_encode(pack('V', mt_rand())), static::NS_SEPARATOR, 5);
@@ -120,7 +123,7 @@ public function clear()
120123
}
121124

122125
try {
123-
return $this->doClear($this->namespace) || $cleared;
126+
return $this->doClear($this->namespace.$prefix) || $cleared;
124127
} catch (\Exception $e) {
125128
CacheItem::log($this->logger, 'Failed to clear the cache: '.$e->getMessage(), ['exception' => $e]);
126129

‎src/Symfony/Component/Cache/Traits/ArrayTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/ArrayTrait.php
+14-2Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,22 @@ public function hasItem($key)
6666

6767
/**
6868
* {@inheritdoc}
69+
*
70+
* @param string $prefix
6971
*/
70-
public function clear()
72+
public function clear(/*string $prefix = ''*/)
7173
{
72-
$this->values = $this->expiries = [];
74+
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
75+
76+
if ('' !== $prefix) {
77+
foreach ($this->values as $key => $value) {
78+
if (0 === strpos($key, $prefix)) {
79+
unset($this->values[$key], $this->expiries[$key]);
80+
}
81+
}
82+
} else {
83+
$this->values = $this->expiries = [];
84+
}
7385

7486
return true;
7587
}

‎src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/FilesystemCommonTrait.php
+9Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ protected function doClear($namespace)
5656
$ok = true;
5757

5858
foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($this->directory, \FilesystemIterator::SKIP_DOTS)) as $file) {
59+
if ('' !== $namespace && 0 !== strpos($this->getFileKey($file), $namespace)) {
60+
continue;
61+
}
62+
5963
$ok = ($file->isDir() || $this->doUnlink($file) || !file_exists($file)) && $ok;
6064
}
6165

@@ -114,6 +118,11 @@ private function getFile($id, $mkdir = false, string $directory = null)
114118
return $dir.substr($hash, 2, 20);
115119
}
116120

121+
private function getFileKey(string $file): string
122+
{
123+
return '';
124+
}
125+
117126
/**
118127
* @internal
119128
*/

‎src/Symfony/Component/Cache/Traits/FilesystemTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/FilesystemTrait.php
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,4 +108,17 @@ protected function doSave(array $values, $lifetime)
108108

109109
return $failed;
110110
}
111+
112+
private function getFileKey(string $file): string
113+
{
114+
if (!$h = @fopen($file, 'rb')) {
115+
return '';
116+
}
117+
118+
fgets($h); // expiry
119+
$encodedKey = fgets($h);
120+
fclose($h);
121+
122+
return rawurldecode(rtrim($encodedKey));
123+
}
111124
}

‎src/Symfony/Component/Cache/Traits/PhpArrayTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/PhpArrayTrait.php
+9-1Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Cache\Traits;
1313

14+
use Symfony\Component\Cache\Adapter\AdapterInterface;
1415
use Symfony\Component\Cache\CacheItem;
1516
use Symfony\Component\Cache\Exception\InvalidArgumentException;
1617
use Symfony\Component\VarExporter\VarExporter;
@@ -121,13 +122,20 @@ public function warmUp(array $values)
121122

122123
/**
123124
* {@inheritdoc}
125+
*
126+
* @param string $prefix
124127
*/
125-
public function clear()
128+
public function clear(/*string $prefix = ''*/)
126129
{
130+
$prefix = 0 < \func_num_args() ? (string) func_get_arg(0) : '';
127131
$this->keys = $this->values = [];
128132

129133
$cleared = @unlink($this->file) || !file_exists($this->file);
130134

135+
if ($this->pool instanceof AdapterInterface) {
136+
return $this->pool->clear($prefix) && $cleared;
137+
}
138+
131139
return $this->pool->clear() && $cleared;
132140
}
133141

‎src/Symfony/Component/Cache/Traits/PhpFilesTrait.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/Cache/Traits/PhpFilesTrait.php
+17-3Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,17 +211,19 @@ protected function doSave(array $values, $lifetime)
211211
$value = var_export($value, true);
212212
}
213213

214+
$encodedKey = rawurlencode($key);
215+
214216
if (!$isStaticValue) {
215217
// We cannot use a closure here because of https://bugs.php.net/76982
216218
$value = str_replace('\Symfony\Component\VarExporter\Internal\\', '', $value);
217-
$value = "<?php\n\nnamespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};\n";
219+
$value = "namespace Symfony\Component\VarExporter\Internal;\n\nreturn \$getExpiry ? {$expiry} : {$value};";
218220
} else {
219-
$value = "<?php return [{$expiry}, {$value}];\n";
221+
$value = "return [{$expiry}, {$value}];";
220222
}
221223

222224
$file = $this->files[$key] = $this->getFile($key, true);
223225
// Since OPcache only compiles files older than the script execution start, set the file's mtime in the past
224-
$ok = $this->write($file, $value, self::$startTime - 10) && $ok;
226+
$ok = $this->write($file, "<?php //{$encodedKey}\n\n{$value}\n", self::$startTime - 10) && $ok;
225227

226228
if ($allowCompile) {
227229
@opcache_invalidate($file, true);
@@ -266,6 +268,18 @@ protected function doUnlink($file)
266268

267269
return @unlink($file);
268270
}
271+
272+
private function getFileKey(string $file): string
273+
{
274+
if (!$h = @fopen($file, 'rb')) {
275+
return '';
276+
}
277+
278+
$encodedKey = substr(fgets($h), 8);
279+
fclose($h);
280+
281+
return rawurldecode(rtrim($encodedKey));
282+
}
269283
}
270284

271285
/**

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.