diff --git a/config/parameters.php b/config/parameters.php index b6ef9061dac..bfde28f7cb5 100644 --- a/config/parameters.php +++ b/config/parameters.php @@ -2,6 +2,7 @@ declare(strict_types=1); +use Rector\Caching\ValueObject\Storage\MemoryCacheStorage; use Rector\Core\Configuration\Option; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; @@ -29,4 +30,11 @@ return static function (ContainerConfigurator $containerConfigurator): void { // cache $parameters->set(Option::CACHE_DIR, sys_get_temp_dir() . '/rector_cached_files'); + + // use faster in-memory cache in CI. + // CI always starts from scratch, therefore IO intensive caching is not worth it + $runsInGithubAction = getenv('GITHUB_ACTION'); + if (false !== $runsInGithubAction) { + $parameters->set(Option::CACHE_CLASS, MemoryCacheStorage::class); + } }; diff --git a/packages-tests/Caching/Detector/config.php b/packages-tests/Caching/Detector/config.php index 24d7b444ad1..ab92831c447 100644 --- a/packages-tests/Caching/Detector/config.php +++ b/packages-tests/Caching/Detector/config.php @@ -2,10 +2,12 @@ declare(strict_types=1); +use Rector\Caching\ValueObject\Storage\MemoryCacheStorage; use Rector\Core\Configuration\Option; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; return static function (ContainerConfigurator $containerConfigurator): void { $parameters = $containerConfigurator->parameters(); $parameters->set(Option::CACHE_DIR, sys_get_temp_dir() . '/_rector_cached_files_test'); + $parameters->set(Option::CACHE_CLASS, MemoryCacheStorage::class); }; diff --git a/packages/Caching/Cache.php b/packages/Caching/Cache.php index e3dc9f127ba..e7f12c75f51 100644 --- a/packages/Caching/Cache.php +++ b/packages/Caching/Cache.php @@ -4,12 +4,12 @@ declare(strict_types=1); namespace Rector\Caching; -use Rector\Caching\ValueObject\Storage\FileCacheStorage; +use Rector\Caching\ValueObject\Storage\CacheStorageInterface; final class Cache { public function __construct( - private FileCacheStorage $fileCacheStorage + private CacheStorageInterface $cacheStorage ) { } @@ -18,7 +18,7 @@ final class Cache */ public function load(string $key, string $variableKey) { - return $this->fileCacheStorage->load($key, $variableKey); + return $this->cacheStorage->load($key, $variableKey); } /** @@ -26,16 +26,16 @@ final class Cache */ public function save(string $key, string $variableKey, $data): void { - $this->fileCacheStorage->save($key, $variableKey, $data); + $this->cacheStorage->save($key, $variableKey, $data); } public function clear(): void { - $this->fileCacheStorage->clear(); + $this->cacheStorage->clear(); } public function clean(string $cacheKey): void { - $this->fileCacheStorage->clean($cacheKey); + $this->cacheStorage->clean($cacheKey); } } diff --git a/packages/Caching/CacheFactory.php b/packages/Caching/CacheFactory.php index 063649f6008..6feec9861ff 100644 --- a/packages/Caching/CacheFactory.php +++ b/packages/Caching/CacheFactory.php @@ -4,7 +4,9 @@ declare(strict_types=1); namespace Rector\Caching; +use Rector\Caching\ValueObject\Storage\CacheStorageInterface; use Rector\Caching\ValueObject\Storage\FileCacheStorage; +use Rector\Caching\ValueObject\Storage\MemoryCacheStorage; use Rector\Core\Configuration\Option; use Symplify\PackageBuilder\Parameter\ParameterProvider; use Symplify\SmartFileSystem\SmartFileSystem; @@ -21,12 +23,21 @@ final class CacheFactory { $cacheDirectory = $this->parameterProvider->provideStringParameter(Option::CACHE_DIR); - // ensure cache directory exists - if (! $this->smartFileSystem->exists($cacheDirectory)) { - $this->smartFileSystem->mkdir($cacheDirectory); + $cacheClass = FileCacheStorage::class; + if ($this->parameterProvider->hasParameter(Option::CACHE_CLASS)) { + $cacheClass = $this->parameterProvider->provideStringParameter(Option::CACHE_CLASS); } - $fileCacheStorage = new FileCacheStorage($cacheDirectory, $this->smartFileSystem); - return new Cache($fileCacheStorage); + if ($cacheClass === FileCacheStorage::class) { + // ensure cache directory exists + if (! $this->smartFileSystem->exists($cacheDirectory)) { + $this->smartFileSystem->mkdir($cacheDirectory); + } + + $fileCacheStorage = new FileCacheStorage($cacheDirectory, $this->smartFileSystem); + return new Cache($fileCacheStorage); + } + + return new Cache(new MemoryCacheStorage()); } } diff --git a/packages/Caching/ValueObject/Storage/CacheStorageInterface.php b/packages/Caching/ValueObject/Storage/CacheStorageInterface.php new file mode 100644 index 00000000000..ff38585936d --- /dev/null +++ b/packages/Caching/ValueObject/Storage/CacheStorageInterface.php @@ -0,0 +1,25 @@ +getCacheFilePaths($key); @@ -79,9 +73,9 @@ final class FileCacheStorage } } - public function clean(string $cacheKey): void + public function clean(string $key): void { - $cacheFilePaths = $this->getCacheFilePaths($cacheKey); + $cacheFilePaths = $this->getCacheFilePaths($key); $this->smartFileSystem->remove([ $cacheFilePaths->getFirstDirectory(), diff --git a/packages/Caching/ValueObject/Storage/MemoryCacheStorage.php b/packages/Caching/ValueObject/Storage/MemoryCacheStorage.php new file mode 100644 index 00000000000..b4faecc9dac --- /dev/null +++ b/packages/Caching/ValueObject/Storage/MemoryCacheStorage.php @@ -0,0 +1,47 @@ + */ + private array $storage = []; + + public function load(string $key, string $variableKey) + { + if (!isset($this->storage[$key])) { + return null; + } + + $item = $this->storage[$key]; + if (!$item->isVariableKeyValid($variableKey)) { + return null; + } + + return $item->getData(); + } + + public function save(string $key, string $variableKey, $data): void + { + $this->storage[$key] = new CacheItem($variableKey, $data); + } + + public function clean(string $key): void + { + if (!isset($this->storage[$key])) { + return; + } + + unset($this->storage[$key]); + } + + public function clear(): void + { + $this->storage = []; + } +} diff --git a/phpstan.neon b/phpstan.neon index 5b832b09a54..07bbb1e5716 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -511,6 +511,12 @@ parameters: - '#Method "decorateReturnWithSpecificType\(\)" returns bool type, so the name should start with is/has/was#' - '#Method "resolveObjectType\(\)" returns bool type, so the name should start with is/has/was#' + # internal contract, not meant for re-use at rector consumer level + - + message: '#Interface must be located in "Contract" namespace#' + paths: + - packages/Caching/ValueObject/Storage/CacheStorageInterface.php + # resolve later - message: '#Use explicit names over dynamic ones#' diff --git a/src/Configuration/Option.php b/src/Configuration/Option.php index 25348ea8ccb..dcf2e2f7df3 100644 --- a/src/Configuration/Option.php +++ b/src/Configuration/Option.php @@ -5,6 +5,8 @@ declare(strict_types=1); namespace Rector\Core\Configuration; use JetBrains\PhpStorm\Immutable; +use Rector\Caching\ValueObject\Storage\CacheStorageInterface; +use Rector\Caching\ValueObject\Storage\FileCacheStorage; use Symplify\Skipper\ValueObject\Option as SkipperOption; #[Immutable] @@ -111,6 +113,14 @@ final class Option */ public const CACHE_DIR = 'cache_dir'; + /** + * Cache backend. Most of the time we cache in files, but in ephemeral environment (e.g. CI), a faster `MemoryCacheStorage` can be usefull. + * + * @var class-string + * @internal + */ + public const CACHE_CLASS = FileCacheStorage::class; + /** * @var string */