From 1fbc51fa20eefa5ac98c61503a84316950f9b309 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 11 Nov 2026 17:46:07 -0700 Subject: [PATCH 1/5] Add tests for flushing the dedicated static_cache store --- tests/StaticCaching/ApplicationCacherTest.php | 26 +++++++++++++++++++ .../StaticCaching/DefaultInvalidatorTest.php | 19 ++++++++++++++ tests/StaticCaching/FileCacherTest.php | 14 ++++++++++ 3 files changed, 59 insertions(+) diff --git a/tests/StaticCaching/ApplicationCacherTest.php b/tests/StaticCaching/ApplicationCacherTest.php index 12694feae75..af7505107df 100644 --- a/tests/StaticCaching/ApplicationCacherTest.php +++ b/tests/StaticCaching/ApplicationCacherTest.php @@ -8,6 +8,7 @@ use Illuminate\Contracts\Cache\Repository; use Illuminate\Http\Request; use Illuminate\Routing\Events\ResponsePrepared; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Queue; use PHPUnit\Framework\Attributes\DataProvider; @@ -610,6 +611,31 @@ public function it_flushes() $this->assertEquals([], $cacher->getUrls('http://another.com')->all()); } + #[Test] + public function flushing_wipes_the_dedicated_static_cache_store() + { + config(['cache.stores.static_cache' => ['driver' => 'array']]); + $cache = Cache::store('static_cache'); + $cache->forever('untracked', 'value'); + $cacher = new ApplicationCacher($cache, ['base_url' => 'http://example.com']); + + $cacher->flush(); + + $this->assertNull($cache->get('untracked')); + } + + #[Test] + public function flushing_leaves_other_keys_in_a_shared_store() + { + $cache = app(Repository::class); + $cache->forever('untracked', 'value'); + $cacher = new ApplicationCacher($cache, ['base_url' => 'http://example.com']); + + $cacher->flush(); + + $this->assertEquals('value', $cache->get('untracked')); + } + #[Test] public function flushing_removes_cached_error_responses() { diff --git a/tests/StaticCaching/DefaultInvalidatorTest.php b/tests/StaticCaching/DefaultInvalidatorTest.php index fab720e4053..6d92ec0b526 100644 --- a/tests/StaticCaching/DefaultInvalidatorTest.php +++ b/tests/StaticCaching/DefaultInvalidatorTest.php @@ -6,6 +6,7 @@ use Illuminate\Http\Request; use Illuminate\Routing\Events\ResponsePrepared; use Illuminate\Support\Carbon; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Queue; use Mockery; use PHPUnit\Framework\Attributes\DataProvider; @@ -55,6 +56,24 @@ public function specifying_all_as_invalidation_rule_will_just_flush_the_cache() $this->assertNull($invalidator->invalidate($item)); } + #[Test] + public function specifying_all_as_invalidation_rule_flushes_the_dedicated_static_cache_store() + { + config([ + 'statamic.static_caching.strategy' => 'half', + 'statamic.static_caching.strategies.half.driver' => 'application', + 'cache.stores.static_cache' => ['driver' => 'array'], + ]); + + Cache::store('static_cache')->forever('untracked', 'value'); + + $invalidator = new Invalidator(app(Cacher::class), 'all'); + + $invalidator->invalidate(Mockery::mock(Entry::class)); + + $this->assertNull(Cache::store('static_cache')->get('untracked')); + } + #[Test] public function assets_can_trigger_url_invalidation() { diff --git a/tests/StaticCaching/FileCacherTest.php b/tests/StaticCaching/FileCacherTest.php index f8992335821..d5679b37ed5 100644 --- a/tests/StaticCaching/FileCacherTest.php +++ b/tests/StaticCaching/FileCacherTest.php @@ -4,6 +4,7 @@ use Illuminate\Contracts\Cache\Repository; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Event; use Illuminate\Support\Facades\Queue; use PHPUnit\Framework\Attributes\DataProvider; @@ -167,6 +168,19 @@ public function flushing_the_cache_deletes_from_all_cache_locations() $writer->shouldHaveReceived('flush')->with('fr/test/path'); } + #[Test] + public function flushing_wipes_the_dedicated_static_cache_store() + { + config(['cache.stores.static_cache' => ['driver' => 'array']]); + $cache = Cache::store('static_cache'); + $cache->forever('untracked', 'value'); + $cacher = $this->fileCacher(['path' => 'test/path'], \Mockery::spy(Writer::class), $cache); + + $cacher->flush(); + + $this->assertNull($cache->get('untracked')); + } + #[Test] public function invalidating_a_url_thats_not_cached_will_do_nothing() { From 5aa2bb0e14495434682da5c1cbf7f5b679b299e5 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 11 Nov 2026 17:46:07 -0700 Subject: [PATCH 2/5] Flush the dedicated static_cache store from the cachers --- src/StaticCaching/Cachers/AbstractCacher.php | 20 +++++++++++++++++++ .../Cachers/ApplicationCacher.php | 2 ++ src/StaticCaching/Cachers/FileCacher.php | 2 ++ src/StaticCaching/StaticCacheManager.php | 4 ---- tests/StaticCaching/ManagerTest.php | 4 ++-- 5 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/StaticCaching/Cachers/AbstractCacher.php b/src/StaticCaching/Cachers/AbstractCacher.php index 34df6bd7409..0db6bafbb16 100644 --- a/src/StaticCaching/Cachers/AbstractCacher.php +++ b/src/StaticCaching/Cachers/AbstractCacher.php @@ -9,6 +9,7 @@ use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; use Illuminate\Http\Response; +use Illuminate\Support\Facades\Cache; use Statamic\Console\Commands\StaticWarmJob; use Statamic\Facades\Site; use Statamic\Facades\URL; @@ -168,6 +169,25 @@ public function flushUrls() $this->cache->forget($this->normalizeKey('domains')); } + /** + * Wipe the dedicated static cache store, when one is in use. + * + * Only the URL index is tracked, so anything else in the store (nocache + * regions, orphaned responses) would otherwise survive a flush. + * + * @return void + */ + protected function flushDedicatedStore() + { + if (! config()->has('cache.stores.static_cache')) { + return; + } + + if ($this->cache === Cache::store('static_cache')) { + $this->cache->flush(); + } + } + /** * Save a URL to the cache. * diff --git a/src/StaticCaching/Cachers/ApplicationCacher.php b/src/StaticCaching/Cachers/ApplicationCacher.php index 8846b595e12..28cd8ed90b1 100644 --- a/src/StaticCaching/Cachers/ApplicationCacher.php +++ b/src/StaticCaching/Cachers/ApplicationCacher.php @@ -146,6 +146,8 @@ public function flush() }); $this->flushUrls(); + + $this->flushDedicatedStore(); } /** diff --git a/src/StaticCaching/Cachers/FileCacher.php b/src/StaticCaching/Cachers/FileCacher.php index 9e1506da520..66630c2ebf1 100644 --- a/src/StaticCaching/Cachers/FileCacher.php +++ b/src/StaticCaching/Cachers/FileCacher.php @@ -133,6 +133,8 @@ public function flush() } $this->flushUrls(); + + $this->flushDedicatedStore(); } /** diff --git a/src/StaticCaching/StaticCacheManager.php b/src/StaticCaching/StaticCacheManager.php index 1e2156c10d9..3fb210f4d49 100644 --- a/src/StaticCaching/StaticCacheManager.php +++ b/src/StaticCaching/StaticCacheManager.php @@ -70,10 +70,6 @@ public function flush() $this->flushNocache(); - if ($this->hasCustomStore()) { - $this->cacheStore()->flush(); - } - StaticCacheCleared::dispatch(); } diff --git a/tests/StaticCaching/ManagerTest.php b/tests/StaticCaching/ManagerTest.php index f42d38f0ad4..f667b6de5b2 100644 --- a/tests/StaticCaching/ManagerTest.php +++ b/tests/StaticCaching/ManagerTest.php @@ -51,7 +51,7 @@ private function getCacherCache(ApplicationCacher $cacher) } #[Test] - public function it_flushes() + public function it_flushes_without_touching_the_static_cache_store_itself() { config([ 'statamic.static_caching.strategy' => 'test', @@ -66,7 +66,7 @@ public function it_flushes() StaticCache::extend('test', fn () => $mock); Cache::shouldReceive('store')->andReturnSelf(); - Cache::shouldReceive('flush')->once(); + Cache::shouldNotReceive('flush'); Cache::shouldReceive('forget')->with('statamic:cache-utility:page-statuses')->once(); StaticCache::flush(); From 261b32ccd8dc50dc802d9b0fefd442875d8dc5ec Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 11 Nov 2026 17:50:02 -0700 Subject: [PATCH 3/5] Use native void return on flushDedicatedStore --- src/StaticCaching/Cachers/AbstractCacher.php | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/StaticCaching/Cachers/AbstractCacher.php b/src/StaticCaching/Cachers/AbstractCacher.php index 0db6bafbb16..fb9698f41dc 100644 --- a/src/StaticCaching/Cachers/AbstractCacher.php +++ b/src/StaticCaching/Cachers/AbstractCacher.php @@ -169,15 +169,7 @@ public function flushUrls() $this->cache->forget($this->normalizeKey('domains')); } - /** - * Wipe the dedicated static cache store, when one is in use. - * - * Only the URL index is tracked, so anything else in the store (nocache - * regions, orphaned responses) would otherwise survive a flush. - * - * @return void - */ - protected function flushDedicatedStore() + protected function flushDedicatedStore(): void { if (! config()->has('cache.stores.static_cache')) { return; From 25ba42bd932b1199d51af92876afe15bb83140fc Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 11 Nov 2026 17:54:40 -0700 Subject: [PATCH 4/5] Keep the manager flush for null and custom static cache drivers --- src/StaticCaching/StaticCacheManager.php | 6 ++++++ tests/StaticCaching/ManagerTest.php | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/StaticCaching/StaticCacheManager.php b/src/StaticCaching/StaticCacheManager.php index 3fb210f4d49..549c95f065c 100644 --- a/src/StaticCaching/StaticCacheManager.php +++ b/src/StaticCaching/StaticCacheManager.php @@ -70,6 +70,12 @@ public function flush() $this->flushNocache(); + // The built-in cachers already wipe the dedicated store from their own flush(), + // but null and custom drivers don't, and flushNocache() relies on it being wiped. + if ($this->hasCustomStore()) { + $this->cacheStore()->flush(); + } + StaticCacheCleared::dispatch(); } diff --git a/tests/StaticCaching/ManagerTest.php b/tests/StaticCaching/ManagerTest.php index f667b6de5b2..f42d38f0ad4 100644 --- a/tests/StaticCaching/ManagerTest.php +++ b/tests/StaticCaching/ManagerTest.php @@ -51,7 +51,7 @@ private function getCacherCache(ApplicationCacher $cacher) } #[Test] - public function it_flushes_without_touching_the_static_cache_store_itself() + public function it_flushes() { config([ 'statamic.static_caching.strategy' => 'test', @@ -66,7 +66,7 @@ public function it_flushes_without_touching_the_static_cache_store_itself() StaticCache::extend('test', fn () => $mock); Cache::shouldReceive('store')->andReturnSelf(); - Cache::shouldNotReceive('flush'); + Cache::shouldReceive('flush')->once(); Cache::shouldReceive('forget')->with('statamic:cache-utility:page-statuses')->once(); StaticCache::flush(); From 392549a43bc4028d9626124f13442c2f2b0caa15 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 11 Nov 2026 18:07:07 -0700 Subject: [PATCH 5/5] Add manager flush test for the null strategy with a dedicated store --- tests/StaticCaching/ManagerTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/StaticCaching/ManagerTest.php b/tests/StaticCaching/ManagerTest.php index f42d38f0ad4..7313fc40867 100644 --- a/tests/StaticCaching/ManagerTest.php +++ b/tests/StaticCaching/ManagerTest.php @@ -72,6 +72,21 @@ public function it_flushes() StaticCache::flush(); } + #[Test] + public function it_flushes_the_dedicated_store_for_drivers_that_do_not_flush_it_themselves() + { + config([ + 'statamic.static_caching.strategy' => 'null', + 'cache.stores.static_cache' => ['driver' => 'array'], + ]); + + Cache::store('static_cache')->forever('nocache::urls', ['/one']); + + StaticCache::flush(); + + $this->assertNull(Cache::store('static_cache')->get('nocache::urls')); + } + #[Test] public function it_flushes_without_static_cache_store() {