diff --git a/CHANGELOG.md b/CHANGELOG.md index c196694..1062814 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ - Enh #88: Don't create cache directory on `FileCache` initialization (@vjik) - Bug #88: Set correct permissions for nested directories (@vjik) - Bug #85: Clear stat cache in `FileCache::set()` (@samdark) +- Bug #86: Handle race condition when creating cache directory (@vjik) ## 3.1.0 October 09, 2023 diff --git a/src/FileCache.php b/src/FileCache.php index 2e6dc85..e65619d 100644 --- a/src/FileCache.php +++ b/src/FileCache.php @@ -20,12 +20,16 @@ use function is_dir; use function is_file; use function iterator_to_array; +use function mkdir; use function opendir; use function posix_geteuid; use function random_int; use function readdir; +use function restore_error_handler; use function rmdir; use function serialize; +use function set_error_handler; +use function sprintf; use function strpbrk; use function substr; use function unlink; @@ -328,10 +332,21 @@ private function ensureDirectory(string $path): void throw new CacheException("Failed to create cache directory, file with the same name exists: \"$path\"."); } - mkdir($path, recursive: true); - - if (!is_dir($path)) { - throw new CacheException("Failed to create cache directory \"$path\"."); + set_error_handler( + static function (int $errorNumber, string $errorString) use ($path): bool { + if (is_dir($path)) { + return true; + } + throw new CacheException( + sprintf('Failed to create directory "%s". %s', $path, $errorString), + $errorNumber, + ); + } + ); + try { + mkdir($path, recursive: true); + } finally { + restore_error_handler(); } chmod($path, $this->directoryMode); diff --git a/tests/FileCacheTest.php b/tests/FileCacheTest.php index b7ada84..a91ab1e 100644 --- a/tests/FileCacheTest.php +++ b/tests/FileCacheTest.php @@ -656,4 +656,13 @@ public function testSetClearsStatCache(): void $this->assertTrue($this->cache->set(__FUNCTION__, 'cache2', 2)); $this->assertSame('cache2', $this->cache->get(__FUNCTION__)); } + + public function testMkdirConvertingErrorToException(): void + { + $cache = new FileCache(''); + + $this->expectException(CacheException::class); + $this->expectExceptionMessage('Failed to create directory "". mkdir(): Invalid path'); + $cache->set('test', 0); + } }