From 8a1cf0a9f41a2a9cb2831d59d75ced0b04e5f40e Mon Sep 17 00:00:00 2001 From: Joshua Blum Date: Mon, 1 Jun 2026 18:52:22 +0200 Subject: [PATCH 1/3] Truncate static cache files before writing --- src/StaticCaching/Cachers/Writer.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/StaticCaching/Cachers/Writer.php b/src/StaticCaching/Cachers/Writer.php index 82ac7558228..45e48095453 100644 --- a/src/StaticCaching/Cachers/Writer.php +++ b/src/StaticCaching/Cachers/Writer.php @@ -43,6 +43,11 @@ public function write($path, $content, $lockFor = 0) return false; } + // Truncate the file before writing. Since the "c" mode doesn't truncate and leaves + // the pointer at the start, writing content shorter than what's already on disk + // would otherwise leave stale bytes behind, producing an invalid document. + ftruncate($handle, 0); + fwrite($handle, $content); chmod($path, $this->permissions['file']); From aaf7aca83ac7bfc810efeaa320b233bc0eedced4 Mon Sep 17 00:00:00 2001 From: Joshua Blum Date: Mon, 1 Jun 2026 18:52:27 +0200 Subject: [PATCH 2/3] Add test --- tests/StaticCaching/WriterTest.php | 48 ++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 tests/StaticCaching/WriterTest.php diff --git a/tests/StaticCaching/WriterTest.php b/tests/StaticCaching/WriterTest.php new file mode 100644 index 00000000000..ee32f14e90c --- /dev/null +++ b/tests/StaticCaching/WriterTest.php @@ -0,0 +1,48 @@ +path = storage_path('framework/testing/static-cache-writer/page.html'); + + @unlink($this->path); + } + + public function tearDown(): void + { + @unlink($this->path); + + parent::tearDown(); + } + + #[Test] + public function it_writes_the_content_to_disk() + { + $written = (new Writer)->write($this->path, 'hello'); + + $this->assertTrue($written); + $this->assertSame('hello', file_get_contents($this->path)); + } + + #[Test] + public function it_truncates_stale_bytes_when_overwriting_with_shorter_content() + { + $writer = new Writer; + + $writer->write($this->path, 'this is a much longer page'); + $writer->write($this->path, 'short'); + + $this->assertSame('short', file_get_contents($this->path)); + } +} From 4c4fef72b5ecb2415198df95fb3d85617c2a009d Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 2 Jun 2026 15:27:48 -0400 Subject: [PATCH 3/3] Handle ftruncate failure and clean up test directory in tearDown Co-Authored-By: Claude Sonnet 4.6 --- src/StaticCaching/Cachers/Writer.php | 6 +++++- tests/StaticCaching/WriterTest.php | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/StaticCaching/Cachers/Writer.php b/src/StaticCaching/Cachers/Writer.php index 45e48095453..2696a120cbb 100644 --- a/src/StaticCaching/Cachers/Writer.php +++ b/src/StaticCaching/Cachers/Writer.php @@ -46,7 +46,11 @@ public function write($path, $content, $lockFor = 0) // Truncate the file before writing. Since the "c" mode doesn't truncate and leaves // the pointer at the start, writing content shorter than what's already on disk // would otherwise leave stale bytes behind, producing an invalid document. - ftruncate($handle, 0); + if (! ftruncate($handle, 0)) { + fclose($handle); + + return false; + } fwrite($handle, $content); chmod($path, $this->permissions['file']); diff --git a/tests/StaticCaching/WriterTest.php b/tests/StaticCaching/WriterTest.php index ee32f14e90c..1121515677e 100644 --- a/tests/StaticCaching/WriterTest.php +++ b/tests/StaticCaching/WriterTest.php @@ -22,6 +22,7 @@ public function setUp(): void public function tearDown(): void { @unlink($this->path); + @rmdir(dirname($this->path)); parent::tearDown(); }