Skip to content

Commit

Permalink
Merge pull request #96 from bobvandevijver/filecache-writable-check
Browse files Browse the repository at this point in the history
Automatically create cache directory when missing
  • Loading branch information
goetas authored Dec 21, 2020
2 parents 491917b + 6af9152 commit f410b75
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 8 deletions.
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"phpunit/phpunit": "^8.5|^9.0",
"psr/container": "^1.0",
"symfony/cache" : "^3.1|^4.0|^5.0",
"symfony/dependency-injection" : "^3.1|^4.0|^5.0"
"symfony/dependency-injection" : "^3.1|^4.0|^5.0",
"mikey179/vfsstream": "^1.6.7"
},
"autoload": {
"psr-4": { "Metadata\\": "src/" }
Expand Down
21 changes: 14 additions & 7 deletions src/Cache/FileCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ class FileCache implements CacheInterface

public function __construct(string $dir)
{
if (!is_dir($dir)) {
throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist.', $dir));
if (!is_dir($dir) && false === @mkdir($dir, 0777, true)) {
throw new \InvalidArgumentException(sprintf('Can\'t create directory for cache at "%s"', $dir));
}

$this->dir = rtrim($dir, '\\/');
}

public function load(string $class): ?ClassMetadata
{
$path = $this->dir . '/' . $this->sanitizeCacheKey($class) . '.cache.php';
$path = $this->getCachePath($class);
if (!file_exists($path)) {
return null;
}
Expand All @@ -49,7 +49,10 @@ public function put(ClassMetadata $metadata): void
throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable.', $this->dir));
}

$path = $this->dir . '/' . $this->sanitizeCacheKey($metadata->name) . '.cache.php';
$path = $this->getCachePath($metadata->name);
if (!is_writable(dirname($path))) {
throw new \RuntimeException(sprintf('Cache file "%s" is not writable.', $path));
}

$tmpFile = tempnam($this->dir, 'metadata-cache');
if (false === $tmpFile) {
Expand Down Expand Up @@ -97,18 +100,22 @@ private function renameFile(string $source, string $target): void

public function evict(string $class): void
{
$path = $this->dir . '/' . $this->sanitizeCacheKey($class) . '.cache.php';
$path = $this->getCachePath($class);
if (file_exists($path)) {
@unlink($path);
}
}

/**
* This function computes the cache file path.
*
* If anonymous class is to be cached, it contains invalid path characters that need to be removed/replaced
* Example of anonymous class name: class@anonymous\x00/app/src/Controller/DefaultController.php0x7f82a7e026ec
*/
private function sanitizeCacheKey(string $key): string
private function getCachePath(string $key): string
{
return str_replace(['\\', "\0", '@', '/', '$', '{', '}', ':'], '-', $key);
$fileName = str_replace(['\\', "\0", '@', '/', '$', '{', '}', ':'], '-', $key);

return $this->dir . '/' . $fileName . '.cache.php';
}
}
37 changes: 37 additions & 0 deletions tests/Cache/FileCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,24 @@
use Metadata\Cache\FileCache;
use Metadata\ClassMetadata;
use Metadata\Tests\Fixtures\TestObject;
use org\bovigo\vfs\vfsStream;
use PHPUnit\Framework\TestCase;

class FileCacheTest extends TestCase
{
private $dir;
private $nestedDir;

protected function setUp(): void
{
$this->dir = sys_get_temp_dir() . '/jms-' . md5(__CLASS__);
$this->nestedDir = $this->dir . '/some-dir';
if (is_dir($this->dir)) {
if (is_dir($this->nestedDir)) {
array_map('unlink', glob($this->nestedDir . '/*'));
rmdir($this->nestedDir);
}

array_map('unlink', glob($this->dir . '/*'));
} else {
mkdir($this->dir);
Expand Down Expand Up @@ -69,4 +77,33 @@ public function testNonReturningCache(string $fileContents)

$this->assertNull($cache->load(TestObject::class));
}

public function testPutCacheFileInNotExistingDirectory()
{
$cache = new FileCache($this->nestedDir);

$reflectionClass = new \ReflectionClass('Metadata\Tests\Fixtures\TestObject');
$cache->put($metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestObject'));
$this->assertEquals($metadata, $cache->load($reflectionClass->name));
}

public function testThrowExceptionIfCacheDirNotWritable()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Can\'t create directory for cache at "vfs://root/JMS"');

$root = vfsStream::setup('root', 0555);
$cache = new FileCache($root->url() . '/JMS');
}

public function testThrowExceptionIfCacheFilePathNotWritable()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('The directory "vfs://root" is not writable.');

$root = vfsStream::setup('root', 0555);
$cache = new FileCache($root->url());

$cache->put($metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestParent'));
}
}

0 comments on commit f410b75

Please sign in to comment.