Skip to content

Commit 7793b07

Browse files
jasonvargaclaude
andauthored
[6.x] Fix nocache database driver failing on MySQL with invalid UTF-8 (#14505)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 08944b1 commit 7793b07

File tree

2 files changed

+93
-2
lines changed

2 files changed

+93
-2
lines changed

src/StaticCaching/NoCache/DatabaseSession.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,17 @@ public function region(string $key): Region
3030
throw new RegionNotFound($key);
3131
}
3232

33-
return unserialize($region->region, ['allowed_classes' => true]);
33+
// Fall back to treating the value as a raw serialized string for rows
34+
// written before base64 encoding was introduced. Strict base64_decode
35+
// reliably detects legacy rows because any serialized PHP value always
36+
// contains ":" or ";", neither of which are in the base64 alphabet.
37+
$decoded = base64_decode($region->region, true);
38+
39+
if ($decoded === false) {
40+
$decoded = $region->region;
41+
}
42+
43+
return unserialize($decoded, ['allowed_classes' => true]);
3444
}
3545

3646
protected function cacheRegion(Region $region)
@@ -39,7 +49,7 @@ protected function cacheRegion(Region $region)
3949
'key' => $region->key(),
4050
], [
4151
'url' => md5($this->url),
42-
'region' => serialize($region),
52+
'region' => base64_encode(serialize($region)),
4353
]);
4454
}
4555
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
3+
namespace Tests\StaticCaching;
4+
5+
use Illuminate\Database\Schema\Blueprint;
6+
use Illuminate\Support\Facades\Schema;
7+
use PHPUnit\Framework\Attributes\Test;
8+
use Statamic\StaticCaching\NoCache\DatabaseRegion;
9+
use Statamic\StaticCaching\NoCache\DatabaseSession;
10+
use Statamic\StaticCaching\NoCache\StringRegion;
11+
use Tests\TestCase;
12+
13+
class NoCacheDatabaseSessionTest extends TestCase
14+
{
15+
protected function setUp(): void
16+
{
17+
parent::setUp();
18+
19+
Schema::create('nocache_regions', function (Blueprint $table) {
20+
$table->string('key')->index()->primary();
21+
$table->string('url')->index();
22+
$table->longText('region');
23+
$table->timestamps();
24+
});
25+
}
26+
27+
public function tearDown(): void
28+
{
29+
Schema::dropIfExists('nocache_regions');
30+
31+
parent::tearDown();
32+
}
33+
34+
#[Test]
35+
public function it_stores_regions_base64_encoded()
36+
{
37+
$session = new DatabaseSession('http://localhost/test');
38+
39+
$region = $session->pushRegion('the contents', ['foo' => 'bar'], '.html');
40+
41+
$stored = DatabaseRegion::where('key', $region->key())->first()->region;
42+
43+
$this->assertEquals(base64_encode(serialize($region)), $stored);
44+
}
45+
46+
#[Test]
47+
public function it_reads_base64_encoded_regions()
48+
{
49+
$session = new DatabaseSession('http://localhost/test');
50+
51+
$region = $session->pushRegion('the contents', ['foo' => 'bar'], '.html');
52+
53+
$retrieved = $session->region($region->key());
54+
55+
$this->assertInstanceOf(StringRegion::class, $retrieved);
56+
$this->assertEquals($region->key(), $retrieved->key());
57+
$this->assertEquals(['foo' => 'bar'], $retrieved->context());
58+
}
59+
60+
#[Test]
61+
public function it_reads_legacy_non_base64_encoded_regions()
62+
{
63+
// Simulate a row written before base64 encoding was introduced
64+
// (e.g. an existing PostgreSQL or SQLite install that ran the
65+
// nocache migration prior to this fix).
66+
$session = new DatabaseSession('http://localhost/test');
67+
$region = new StringRegion($session, 'the contents', ['foo' => 'bar'], '.html');
68+
69+
DatabaseRegion::create([
70+
'key' => $region->key(),
71+
'url' => md5('http://localhost/test'),
72+
'region' => serialize($region),
73+
]);
74+
75+
$retrieved = $session->region($region->key());
76+
77+
$this->assertInstanceOf(StringRegion::class, $retrieved);
78+
$this->assertEquals($region->key(), $retrieved->key());
79+
$this->assertEquals(['foo' => 'bar'], $retrieved->context());
80+
}
81+
}

0 commit comments

Comments
 (0)