diff --git a/README.md b/README.md index ff8ed6a..abe845c 100644 --- a/README.md +++ b/README.md @@ -208,20 +208,20 @@ name in the PHP class. ## Caching You can (**and should**) cache the PKL modules to improve performance. This is especially useful when evaluating the same PKL file -multiple times. You can use the `dump` command to dump the PKL module to a cache file. Phikl will then use the cache file automatically when evaluating a PKL file. If the PKL file is not found in the cache, Phikl will evaluate the PKL file on the go. +multiple times. You can use the `warmup` command to dump the PKL modules to a cache file. Phikl will then use the cache file automatically when evaluating a PKL file. If the PKL file is not found in the cache, Phikl will evaluate the PKL file on the go. **⚠️ Using Phikl with the cache avoids the PKL CLI tool to be executed to evaluate modules and should be done when deploying your application for better performances.** Phikl will go through all `.pkl` files of your project and dump them to the cache file. -Here's an example of how to use the `dump` command: +Here's an example of how to use the `warmup` command: ```bash -vendor/bin/phikl dump +vendor/bin/phikl warmup # you can also specify the file if you want to use a custom location # don't forget to set the `PHIKL_CACHE_FILE` environment variable -vendor/bin/phikl dump --cache-file=cache/pkl.cache +vendor/bin/phikl warmup --cache-file=cache/pkl.cache ``` If you need to validate a cache file, you can do so by using the `validate-cache` command: @@ -237,7 +237,7 @@ vendor/bin/phikl validate-cache --cache-file=.cache/.phikl Here are a few things to note about Phikl cache: - You can disable the cache by calling `Pkl::toggleCache(false)`, which is useful for development but highly discouraged in production -- Phikl will automatically invalidate the cache if the PKL file is modified or if the cache was generated with a different version of the Pkl CLI tool +- Phikl will automatically refresh the cache if a PKL module is modified since last warmup - Any corrupted cache entry will be automatically refreshed If you have your own cache system, you can use the `Pkl::setCache()` method to set the cache system to use. You can pass it any instance of compliant PSR-16 cache system implementing `Psr\SimpleCache\CacheInterface`. This is useful you want to use, for example, a Redis server as a cache system for your Pkl modules. diff --git a/composer.json b/composer.json index d5d47b2..8893740 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "psr-4": { "Phikl\\": "src", "Phikl\\Tests\\": "tests" } }, "scripts": { - "test": "./vendor/bin/phpunit", + "test": "./vendor/bin/phpunit --display-warnings", "cs": "./vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --diff --allow-risky=yes", "stan": "vendor/bin/phpstan analyse src tests -l 8" }, diff --git a/src/Cache/PersistentCache.php b/src/Cache/PersistentCache.php index e1a4d92..61a58fb 100644 --- a/src/Cache/PersistentCache.php +++ b/src/Cache/PersistentCache.php @@ -160,7 +160,16 @@ public function load(): void return; } - $this->entries = unserialize($content, ['allowed_classes' => [self::class, Entry::class]]) ?: []; + $unserialized = @unserialize($content, ['allowed_classes' => [self::class, Entry::class]]); + if ($unserialized === false) { + // the cache is unreadable, erase everything + $this->entries = null; + @unlink($cacheFile); + + return; + } + + $this->entries = $unserialized; } /** diff --git a/src/Exception/CorruptedCacheException.php b/src/Exception/CorruptedCacheException.php index d039f48..b3fe1df 100644 --- a/src/Exception/CorruptedCacheException.php +++ b/src/Exception/CorruptedCacheException.php @@ -13,6 +13,6 @@ class CorruptedCacheException extends \RuntimeException { public function __construct(string $cacheFile) { - parent::__construct(sprintf('The cache file "%s" seems corrupted and should be generated again with the `phikl dump` command.', $cacheFile)); + parent::__construct(sprintf('The cache file "%s" seems corrupted and should be generated again with the `phikl warmup` command.', $cacheFile)); } } diff --git a/src/Exception/EmptyCacheException.php b/src/Exception/EmptyCacheException.php index 3580b77..adc5ac5 100644 --- a/src/Exception/EmptyCacheException.php +++ b/src/Exception/EmptyCacheException.php @@ -13,6 +13,6 @@ class EmptyCacheException extends \RuntimeException { public function __construct(string $cacheFile) { - parent::__construct(sprintf('The cache file "%s" is empty or does not exist and should be generated again with the `phikl dump` command.', $cacheFile)); + parent::__construct(sprintf('The cache file "%s" is empty or does not exist and should be generated again with the `phikl warmup` command.', $cacheFile)); } } diff --git a/src/Internal/Command/Runner.php b/src/Internal/Command/Runner.php index 148f847..a9e6277 100644 --- a/src/Internal/Command/Runner.php +++ b/src/Internal/Command/Runner.php @@ -35,8 +35,8 @@ public static function run(InputInterface $input, OutputInterface $output): int return self::version($input, $output); } elseif ($input->getArgument('subcommand') === 'eval') { return self::eval($input, $output); - } elseif ($input->getArgument('subcommand') === 'dump') { - return self::dump($input, $output); + } elseif ($input->getArgument('subcommand') === 'warmup') { + return self::warmup($input, $output); } elseif ($input->getArgument('subcommand') === 'validate-cache') { return self::validateCache($input, $output); } @@ -101,15 +101,15 @@ private static function eval(InputInterface $input, OutputInterface $output): in return Command::SUCCESS; } - private static function dump(InputInterface $input, OutputInterface $output): int + private static function warmup(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); $cacheFile = $input->getOption('cache-file') ?? self::guessCacheFile(); try { - $count = Pkl::dump($cacheFile); + $count = Pkl::warmup($cacheFile); - $io->success(sprintf('%d files dumped to "%s" cache file.', $count, $cacheFile)); + $io->success(sprintf('%d files warmed up to "%s" cache file.', $count, $cacheFile)); if ($cacheFile !== '.phikl.cache') { $io->caution('Make sure to declare the PHIKL_CACHE_FILE environment variable to use the cache file.'); @@ -129,7 +129,7 @@ private static function validateCache(InputInterface $input, OutputInterface $ou $cacheFile = $input->getOption('cache-file') ?? self::guessCacheFile(); if (!file_exists($cacheFile)) { - $io->warning(sprintf('Cache file "%s" does not exist, it can be generated with the `phikl dump` command.', $cacheFile)); + $io->warning(sprintf('Cache file "%s" does not exist, it can be generated with the `phikl warmup` command.', $cacheFile)); return Command::FAILURE; } diff --git a/src/Pkl.php b/src/Pkl.php index 59fab13..5016fd9 100644 --- a/src/Pkl.php +++ b/src/Pkl.php @@ -151,9 +151,9 @@ public static function rawEval(string ...$modules): string * The cache file is used to avoid calling the PKL CLI tool on every * `Pkl::eval()` call. * - * @return int the number of dumped files + * @return int the number of warmed up files */ - public static function dump(string $cacheFile): int + public static function warmup(string $cacheFile): int { self::initExecutable(); diff --git a/tests/Cache/PersistentCacheTest.php b/tests/Cache/PersistentCacheTest.php index 0794aa1..f919838 100644 --- a/tests/Cache/PersistentCacheTest.php +++ b/tests/Cache/PersistentCacheTest.php @@ -194,6 +194,20 @@ public function testGetCacheFile(): void $this->assertSame('.phikl.cache', $cache->getCacheFile()); } + public function testLoadWithCorruptedCacheFile(): void + { + $cache = new PersistentCache(); + $cache->set('key', new Entry('content', 'hash', 0)); + $cache->save(); + + $cacheFile = $cache->getCacheFile(); + file_put_contents($cacheFile, 'invalid'); + + $cache->load(); + $this->assertNull($cache->get('key')); + $this->assertFileDoesNotExist($cacheFile); + } + public function testSetCacheFile(): void { $cache = new PersistentCache(); @@ -260,7 +274,7 @@ public function testValidateOnInvalidCache(): void $cache->save(); $this->expectException(CorruptedCacheException::class); - $this->expectExceptionMessage('The cache file ".phikl.cache" seems corrupted and should be generated again with the `phikl dump` command.'); + $this->expectExceptionMessage('The cache file ".phikl.cache" seems corrupted and should be generated again with the `phikl warmup` command.'); $cache->validate(); } diff --git a/tests/PhiklCommandTest.php b/tests/PhiklCommandTest.php index bed2e0e..6af8e45 100644 --- a/tests/PhiklCommandTest.php +++ b/tests/PhiklCommandTest.php @@ -52,7 +52,7 @@ public function testCanEval(): void public function testCanDump(): void { - $process = new Process(['php', __DIR__.'/../phikl', 'dump', 'tests/Fixtures/simple.pkl']); + $process = new Process(['php', __DIR__.'/../phikl', 'warmup', 'tests/Fixtures/simple.pkl']); $process->mustRun(); $this->assertFileExists('.phikl.cache'); @@ -61,7 +61,7 @@ public function testCanDump(): void public function testCanValidateCache(): void { - $process = new Process(['php', __DIR__.'/../phikl', 'dump', 'tests/Fixtures/simple.pkl']); + $process = new Process(['php', __DIR__.'/../phikl', 'warmup', 'tests/Fixtures/simple.pkl']); $process->mustRun(); $process = new Process(['php', __DIR__.'/../phikl', 'validate-cache']);