Skip to content

Commit 6a0b264

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 6af97d2 commit 6a0b264

2 files changed

Lines changed: 20 additions & 1 deletion

File tree

‎src/Process/Restart/OpcacheSettings.php‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
use function ini_get;
2323
use function is_dir;
2424
use function is_link;
25+
use function is_writable;
2526
use function max;
2627
use function mkdir;
2728
use function phpversion;
@@ -296,7 +297,12 @@ public static function isPrivateDirectory(string $directory, int $userId): bool
296297
}
297298

298299
$permissions = @fileperms($directory);
299-
return @fileowner($directory) === $userId && $permissions !== false && ($permissions & 0022) === 0;
300+
if (@fileowner($directory) !== $userId || $permissions === false || ($permissions & 0022) !== 0) {
301+
return false;
302+
}
303+
304+
// OPcache refuses to start at all - exit code 254 - when it cannot write to opcache.file_cache
305+
return is_writable($directory);
300306
}
301307

302308
/**

‎tests/PHPStan/Process/Restart/OpcacheSettingsTest.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 OpcacheSettingsTest 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(OpcacheSettings::isPrivateDirectory($base . '/shared', $userId));
186191

192+
mkdir($base . '/readonly', 0500);
193+
$this->assertFalse(OpcacheSettings::isPrivateDirectory($base . '/readonly', $userId));
194+
chmod($base . '/readonly', 0700);
195+
187196
symlink($base . '/private', $base . '/link');
188197
$this->assertFalse(OpcacheSettings::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)