Skip to content

Commit 79bcd9b

Browse files
SanderMullerclaude
authored andcommitted
Leave the file cache off when its directory is not writable
OPcache refuses to start, with exit code 254, when it cannot write to opcache.file_cache. The directory tests are skipped on Windows, which has no file cache and where ownership and permission bits mean something else. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent b3f2632 commit 79bcd9b

2 files changed

Lines changed: 19 additions & 1 deletion

File tree

‎src/Turbo/TurboProcessRestarter.php‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
use function is_dir;
2424
use function is_link;
2525
use function is_string;
26+
use function is_writable;
2627
use function max;
2728
use function mkdir;
2829
use function pcntl_exec;
@@ -416,8 +417,12 @@ public static function isPrivateDirectory(string $directory, int $userId): bool
416417
}
417418

418419
$permissions = @fileperms($directory);
420+
if (@fileowner($directory) !== $userId || $permissions === false || ($permissions & 0022) !== 0) {
421+
return false;
422+
}
419423

420-
return @fileowner($directory) === $userId && $permissions !== false && ($permissions & 0022) === 0;
424+
// OPcache refuses to start at all - exit code 254 - when it cannot write to opcache.file_cache
425+
return is_writable($directory);
421426
}
422427

423428
/**

‎tests/PHPStan/Turbo/TurboProcessRestarterTest.php‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use function touch;
2020
use function uniqid;
2121
use function unlink;
22+
use const DIRECTORY_SEPARATOR;
2223

2324
final class TurboProcessRestarterTest extends PHPStanTestCase
2425
{
@@ -173,6 +174,10 @@ public function testResolveContinuousIntegration(array $environment, bool $expec
173174

174175
public function testIsPrivateDirectory(): void
175176
{
177+
if (DIRECTORY_SEPARATOR !== '/') {
178+
$this->markTestSkipped('There is no file cache on Windows, and its permissions do not map to these checks.');
179+
}
180+
176181
$base = self::createTemporaryDirectory();
177182
$userId = (int) fileowner($base);
178183

@@ -184,6 +189,10 @@ public function testIsPrivateDirectory(): void
184189
chmod($base . '/shared', 0777);
185190
$this->assertFalse(TurboProcessRestarter::isPrivateDirectory($base . '/shared', $userId));
186191

192+
mkdir($base . '/readonly', 0500);
193+
$this->assertFalse(TurboProcessRestarter::isPrivateDirectory($base . '/readonly', $userId));
194+
chmod($base . '/readonly', 0700);
195+
187196
symlink($base . '/private', $base . '/link');
188197
$this->assertFalse(TurboProcessRestarter::isPrivateDirectory($base . '/link', $userId));
189198

@@ -195,6 +204,10 @@ public function testIsPrivateDirectory(): void
195204

196205
public function testPruneFileCacheDirectories(): void
197206
{
207+
if (DIRECTORY_SEPARATOR !== '/') {
208+
$this->markTestSkipped('There is no file cache on Windows, and the test needs symlinks.');
209+
}
210+
198211
$base = self::createTemporaryDirectory();
199212
$now = time();
200213
foreach (['current' => 30, 'stale' => 8, 'recent' => 2] as $name => $daysAgo) {

0 commit comments

Comments
 (0)