diff --git a/src/StaticCaching/NoCache/DatabaseSession.php b/src/StaticCaching/NoCache/DatabaseSession.php index c5b14b76851..9e32d8caa95 100644 --- a/src/StaticCaching/NoCache/DatabaseSession.php +++ b/src/StaticCaching/NoCache/DatabaseSession.php @@ -30,7 +30,7 @@ public function region(string $key): Region throw new RegionNotFound($key); } - return unserialize($region->region); + return unserialize($region->region, ['allowed_classes' => true]); } protected function cacheRegion(Region $region) diff --git a/src/StaticCaching/NoCache/Session.php b/src/StaticCaching/NoCache/Session.php index 1b6bce0f3e0..2935e163d37 100644 --- a/src/StaticCaching/NoCache/Session.php +++ b/src/StaticCaching/NoCache/Session.php @@ -51,7 +51,13 @@ public function regions(): Collection public function region(string $key): Region { if ($this->regions->contains($key) && ($region = StaticCache::cacheStore()->get('nocache::region.'.$key))) { - return $region; + if ($region instanceof Region) { + return $region; + } + + if (is_string($region)) { + return unserialize($region, ['allowed_classes' => true]); + } } throw new RegionNotFound($key); @@ -150,6 +156,6 @@ protected function resolvePageAndPathForPagination(): void protected function cacheRegion(Region $region) { - StaticCache::cacheStore()->forever('nocache::region.'.$region->key(), $region); + StaticCache::cacheStore()->forever('nocache::region.'.$region->key(), serialize($region)); } } diff --git a/tests/StaticCaching/NoCacheSessionTest.php b/tests/StaticCaching/NoCacheSessionTest.php index 507825fa8a2..a0b755c27c4 100644 --- a/tests/StaticCaching/NoCacheSessionTest.php +++ b/tests/StaticCaching/NoCacheSessionTest.php @@ -6,7 +6,9 @@ use Illuminate\Support\Facades\Cache; use Mockery; use PHPUnit\Framework\Attributes\Test; +use Statamic\Facades\StaticCache; use Statamic\StaticCaching\Cacher; +use Statamic\StaticCaching\NoCache\RegionNotFound; use Statamic\StaticCaching\NoCache\Session; use Statamic\StaticCaching\NoCache\StringRegion; use Tests\FakesContent; @@ -148,6 +150,39 @@ public function it_restores_from_cache() $this->assertEquals('http://localhost/cp', $cascade['cp_url']); } + #[Test] + public function it_serializes_and_unserializes_regions_through_cache() + { + $session = new Session('http://localhost/test'); + + $region = $session->pushRegion('the contents', ['foo' => 'bar'], '.html'); + + $cached = StaticCache::cacheStore()->get('nocache::region.'.$region->key()); + $this->assertIsString($cached, 'Region should be stored as a serialized string, not an object.'); + + $retrieved = $session->region($region->key()); + + $this->assertInstanceOf(StringRegion::class, $retrieved); + $this->assertEquals($region->key(), $retrieved->key()); + $this->assertEquals(['foo' => 'bar'], $retrieved->context()); + } + + #[Test] + public function it_throws_region_not_found_when_cached_region_is_an_incomplete_class() + { + $session = new Session('http://localhost/test'); + + $region = $session->pushRegion('the contents', ['foo' => 'bar'], '.html'); + + // Simulate what happens when serializable_classes enforcement + // turns a cached Region object into __PHP_Incomplete_Class. + StaticCache::cacheStore()->forever('nocache::region.'.$region->key(), new \__PHP_Incomplete_Class); + + $this->expectException(RegionNotFound::class); + + $session->region($region->key()); + } + #[Test] public function a_singleton_is_bound_in_the_container() {