move from nette/caching to own cache based on PHPStan (#202)

This commit is contained in:
Tomas Votruba 2021-06-11 12:39:28 +02:00 committed by GitHub
parent 9dea8d8cd8
commit ac81e98fbf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 281 additions and 95 deletions

View File

@ -16,7 +16,6 @@
"doctrine/inflector": "^2.0",
"ergebnis/json-printer": "^3.1",
"idiosyncratic/editorconfig": "^0.1.3",
"nette/caching": "^3.1",
"nette/utils": "^3.2",
"nikic/php-parser": "4.10.5",
"phpstan/phpdoc-parser": "^0.5.4",

View File

@ -18,6 +18,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
__DIR__ . '/../packages/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php',
__DIR__ . '/../packages/Testing/PHPUnit',
__DIR__ . '/../packages/BetterPhpDocParser/PhpDoc',
__DIR__ . '/../packages/Caching/Cache.php',
__DIR__ . '/../packages/NodeTypeResolver/NodeVisitor/FileNodeVisitor.php',
// used in PHPStan

View File

@ -8,7 +8,6 @@ use Doctrine\Inflector\Rules\English\InflectorFactory;
use Ergebnis\Json\Printer\Printer;
use Ergebnis\Json\Printer\PrinterInterface;
use Idiosyncratic\EditorConfig\EditorConfig;
use Nette\Caching\Cache;
use PhpParser\BuilderFactory;
use PhpParser\Lexer;
use PhpParser\NodeFinder;
@ -26,7 +25,7 @@ use PHPStan\PhpDocParser\Parser\TypeParser;
use PHPStan\Reflection\ReflectionProvider;
use Rector\BetterPhpDocParser\PhpDocParser\BetterPhpDocParser;
use Rector\BetterPhpDocParser\PhpDocParser\BetterTypeParser;
use Rector\Caching\Cache\NetteCacheFactory;
use Rector\Caching\CacheFactory;
use Rector\Core\Console\ConsoleApplication;
use Rector\Core\PhpParser\Parser\NikicPhpParserFactory;
use Rector\Core\PhpParser\Parser\PhpParserLexerFactory;
@ -128,8 +127,8 @@ return static function (ContainerConfigurator $containerConfigurator): void {
$services->set(FileHelper::class)
->factory([service(PHPStanServicesFactory::class), 'createFileHelper']);
$services->set(Cache::class)
->factory([service(NetteCacheFactory::class), 'create']);
$services->set(\Rector\Caching\Cache::class)
->factory([service(CacheFactory::class), 'create']);
// type resolving
$services->set(IntermediateSourceLocator::class);

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Rector\Caching;
use Rector\Caching\ValueObject\Storage\FileCacheStorage;
final class Cache
{
public function __construct(
private FileCacheStorage $fileCacheStorage
) {
}
/**
* @return mixed|null
*/
public function load(string $key, string $variableKey)
{
return $this->fileCacheStorage->load($key, $variableKey);
}
/**
* @param mixed $data
*/
public function save(string $key, string $variableKey, $data): void
{
$this->fileCacheStorage->save($key, $variableKey, $data);
}
public function clear(): void
{
$this->fileCacheStorage->clear();
}
public function clean(string $cacheKey): void
{
$this->fileCacheStorage->clean($cacheKey);
}
}

View File

@ -2,15 +2,14 @@
declare(strict_types=1);
namespace Rector\Caching\Cache;
namespace Rector\Caching;
use Nette\Caching\Cache;
use Nette\Caching\Storages\FileStorage;
use Rector\Caching\ValueObject\Storage\FileCacheStorage;
use Rector\Core\Configuration\Option;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
use Symplify\SmartFileSystem\SmartFileSystem;
final class NetteCacheFactory
final class CacheFactory
{
public function __construct(
private ParameterProvider $parameterProvider,
@ -27,9 +26,7 @@ final class NetteCacheFactory
$this->smartFileSystem->mkdir($cacheDirectory);
}
$fileStorage = new FileStorage($cacheDirectory);
// namespace is unique per project
return new Cache($fileStorage, getcwd());
$fileCacheStorage = new FileCacheStorage($cacheDirectory, $this->smartFileSystem);
return new Cache($fileCacheStorage);
}
}

View File

@ -15,7 +15,6 @@ use Symplify\SmartFileSystem\SmartFileInfo;
/**
* Inspired by https://github.com/symplify/easy-coding-standard/blob/e598ab54686e416788f28fcfe007fd08e0f371d9/packages/changed-files-detector/src/FileHashComputer.php
* @see \Rector\Caching\Tests\Config\FileHashComputerTest
*/
final class FileHashComputer
{

View File

@ -4,22 +4,19 @@ declare(strict_types=1);
namespace Rector\Caching\Detector;
use Nette\Caching\Cache;
use Nette\Utils\Strings;
use Rector\Caching\Cache;
use Rector\Caching\Config\FileHashComputer;
use Rector\Caching\Enum\CacheKey;
use Symplify\SmartFileSystem\SmartFileInfo;
/**
* Inspired by https://github.com/symplify/symplify/pull/90/files#diff-72041b2e1029a08930e13d79d298ef11
* @see \Rector\Caching\Tests\Detector\ChangedFilesDetectorTest
*
* @see \Rector\Tests\Caching\Detector\ChangedFilesDetectorTest
*/
final class ChangedFilesDetector
{
/**
* @var string
*/
private const CONFIGURATION_HASH_KEY = 'configuration_hash';
public function __construct(
private FileHashComputer $fileHashComputer,
private Cache $cache
@ -34,8 +31,8 @@ final class ChangedFilesDetector
$fileInfoCacheKey = $this->getFileInfoCacheKey($smartFileInfo);
$hash = $this->hashFile($smartFileInfo);
$this->cache->save($fileInfoCacheKey, $hash);
$this->cache->save($fileInfoCacheKey . '_files', $dependentFiles);
$this->cache->save($fileInfoCacheKey, CacheKey::FILE_HASH_KEY, $hash);
$this->cache->save($fileInfoCacheKey . '_files', CacheKey::DEPENDENT_FILES_KEY, $dependentFiles);
}
public function hasFileChanged(SmartFileInfo $smartFileInfo): bool
@ -44,21 +41,19 @@ final class ChangedFilesDetector
$fileInfoCacheKey = $this->getFileInfoCacheKey($smartFileInfo);
$cachedValue = $this->cache->load($fileInfoCacheKey);
$cachedValue = $this->cache->load($fileInfoCacheKey, CacheKey::FILE_HASH_KEY);
return $currentFileHash !== $cachedValue;
}
public function invalidateFile(SmartFileInfo $smartFileInfo): void
{
$fileInfoCacheKey = $this->getFileInfoCacheKey($smartFileInfo);
$this->cache->remove($fileInfoCacheKey);
$this->cache->clean($fileInfoCacheKey);
}
public function clear(): void
{
$this->cache->clean([
Cache::ALL => true,
]);
$this->cache->clear();
}
/**
@ -68,7 +63,7 @@ final class ChangedFilesDetector
{
$fileInfoCacheKey = $this->getFileInfoCacheKey($fileInfo);
$cacheValue = $this->cache->load($fileInfoCacheKey . '_files');
$cacheValue = $this->cache->load($fileInfoCacheKey . '_files', CacheKey::DEPENDENT_FILES_KEY);
if ($cacheValue === null) {
return [];
}
@ -109,15 +104,15 @@ final class ChangedFilesDetector
private function storeConfigurationDataHash(SmartFileInfo $fileInfo, string $configurationHash): void
{
$key = self::CONFIGURATION_HASH_KEY . '_' . Strings::webalize($fileInfo->getRealPath());
$key = CacheKey::CONFIGURATION_HASH_KEY . '_' . Strings::webalize($fileInfo->getRealPath());
$this->invalidateCacheIfConfigurationChanged($key, $configurationHash);
$this->cache->save($key, $configurationHash);
$this->cache->save($key, CacheKey::CONFIGURATION_HASH_KEY, $configurationHash);
}
private function invalidateCacheIfConfigurationChanged(string $key, string $configurationHash): void
{
$oldCachedValue = $this->cache->load($key);
$oldCachedValue = $this->cache->load($key, CacheKey::CONFIGURATION_HASH_KEY);
if ($oldCachedValue === $configurationHash) {
return;
}

View File

@ -0,0 +1,26 @@
<?php
declare(strict_types=1);
namespace Rector\Caching\Enum;
/**
* @enum
*/
final class CacheKey
{
/**
* @var string
*/
public const CONFIGURATION_HASH_KEY = 'configuration_hash';
/**
* @var string
*/
public const FILE_HASH_KEY = 'file_hash';
/**
* @var string
*/
public const DEPENDENT_FILES_KEY = 'dependency_files_key';
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Rector\Caching\ValueObject;
final class CacheFilePaths
{
public function __construct(
private string $firstDirectory,
private string $secondDirectory,
private string $filePath
) {
}
public function getFirstDirectory(): string
{
return $this->firstDirectory;
}
public function getSecondDirectory(): string
{
return $this->secondDirectory;
}
public function getFilePath(): string
{
return $this->filePath;
}
}

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace Rector\Caching\ValueObject;
/**
* Inspired by
* https://github.com/phpstan/phpstan-src/commit/eeae2da7999b2e8b7b04542c6175d46f80c6d0b9#diff-6dc14f6222bf150e6840ca44a7126653052a1cedc6a149b4e5c1e1a2c80eacdc
*/
final class CacheItem
{
/**
* @param mixed $data
*/
public function __construct(
private string $variableKey,
private $data
) {
}
/**
* @param mixed[] $properties
*/
public static function __set_state(array $properties): self
{
return new self($properties['variableKey'], $properties['data']);
}
public function isVariableKeyValid(string $variableKey): bool
{
return $this->variableKey === $variableKey;
}
/**
* @return mixed
*/
public function getData()
{
return $this->data;
}
}

View File

@ -0,0 +1,107 @@
<?php
declare(strict_types=1);
namespace Rector\Caching\ValueObject\Storage;
use Nette\Utils\Random;
use Nette\Utils\Strings;
use Rector\Caching\ValueObject\CacheFilePaths;
use Rector\Caching\ValueObject\CacheItem;
use Symplify\EasyCodingStandard\Caching\Exception\CachingException;
use Symplify\SmartFileSystem\SmartFileSystem;
/**
* Inspired by
* https://github.com/phpstan/phpstan-src/commit/4df7342f3a0aaef4bcd85456dd20ca88d38dd90d#diff-6dc14f6222bf150e6840ca44a7126653052a1cedc6a149b4e5c1e1a2c80eacdc
*/
final class FileCacheStorage
{
public function __construct(
private string $directory,
private SmartFileSystem $smartFileSystem
) {
}
/**
* @return mixed|null
*/
public function load(string $key, string $variableKey)
{
$cacheFilePaths = $this->getCacheFilePaths($key);
$filePath = $cacheFilePaths->getFilePath();
if (! is_file($filePath)) {
return null;
}
$cacheItem = require $filePath;
if (! $cacheItem instanceof CacheItem) {
return null;
}
if (! $cacheItem->isVariableKeyValid($variableKey)) {
return null;
}
return $cacheItem->getData();
}
/**
* @param mixed $data
*/
public function save(string $key, string $variableKey, $data): void
{
$cacheFilePaths = $this->getCacheFilePaths($key);
$this->smartFileSystem->mkdir($cacheFilePaths->getFirstDirectory());
$this->smartFileSystem->mkdir($cacheFilePaths->getSecondDirectory());
$tmpPath = sprintf('%s/%s.tmp', $this->directory, Random::generate());
$errorBefore = error_get_last();
$exported = @var_export(new CacheItem($variableKey, $data), true);
$errorAfter = error_get_last();
if ($errorAfter !== null && $errorBefore !== $errorAfter) {
$errorMessage = sprintf(
'Error occurred while saving item "%s" ("%s") to cache: "%s"',
$key,
$variableKey,
$errorAfter['message']
);
throw new CachingException($errorMessage);
}
$variableFileContent = sprintf("<?php declare(strict_types = 1);\n\nreturn %s;", $exported);
$this->smartFileSystem->dumpFile($tmpPath, $variableFileContent);
$this->smartFileSystem->rename($tmpPath, $cacheFilePaths->getFilePath(), true);
$this->smartFileSystem->remove($tmpPath);
}
public function clean(string $cacheKey): void
{
$cacheFilePaths = $this->getCacheFilePaths($cacheKey);
$this->smartFileSystem->remove([
$cacheFilePaths->getFirstDirectory(),
$cacheFilePaths->getSecondDirectory(),
$cacheFilePaths->getFilePath(),
]);
}
public function clear(): void
{
$this->smartFileSystem->remove($this->directory);
}
private function getCacheFilePaths(string $key): CacheFilePaths
{
$keyHash = sha1($key);
$firstDirectory = sprintf('%s/%s', $this->directory, Strings::substring($keyHash, 0, 2));
$secondDirectory = sprintf('%s/%s', $firstDirectory, Strings::substring($keyHash, 2, 2));
$filePath = sprintf('%s/%s.php', $secondDirectory, $keyHash);
return new CacheFilePaths($firstDirectory, $secondDirectory, $filePath);
}
}

View File

@ -487,3 +487,16 @@ parameters:
-
message: '#Class with base "StaticTypeMapper" name is already used in "Rector\\StaticTypeMapper\\StaticTypeMapper", "Rector\\PHPStanStaticTypeMapper\\TypeMapper\\StaticTypeMapper"\. Use unique name to make classes easy to recognize#'
path: packages/PHPStanStaticTypeMapper/TypeMapper/StaticTypeMapper.php #21
- '#"@var_export\(new \\Rector\\Caching\\ValueObject\\CacheItem\(\$variableKey, \$data\), true\)" is forbidden to use#'
# resolve same named classes later
- '#Class with base "UnionTypeMapper" name is already used in "Rector\\StaticTypeMapper\\PhpDocParser\\UnionTypeMapper", "Rector\\PHPStanStaticTypeMapper\\TypeMapper\\UnionTypeMapper"\. Use unique name to make classes easy to recognize#'
# checking internal class
-
message: '#Instead of "instanceof/is_a\(\)" use ReflectionProvider service or "\(new ObjectType\(<desired_type\>\)\)\-\>isSuperTypeOf\(<element_type\>\)" for static reflection to work#'
path: packages/Caching/ValueObject/Storage/FileCacheStorage.php
# cache hacking
- '#"@var_export\(new \\Symplify\\EasyCodingStandard\\Caching\\ValueObject\\CacheItem\(\$variableKey, \$data\), true\)" is forbidden to use#'

View File

@ -7,7 +7,6 @@ namespace Rector\DeadCode\Rector\If_;
use PhpParser\Node;
use PhpParser\Node\Expr\BooleanNot;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\If_;
use PHPStan\Analyser\Scope;
use Rector\Core\NodeManipulator\IfManipulator;

View File

@ -4,9 +4,7 @@ declare(strict_types=1);
namespace Rector\Core\FileSystem;
use Nette\Caching\Cache;
use Nette\Utils\Strings;
use Rector\Core\Configuration\Configuration;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Symplify\Skipper\SkipCriteriaResolver\SkippedPathsResolver;
@ -36,8 +34,6 @@ final class FilesFinder
private FinderSanitizer $finderSanitizer,
private FileSystemFilter $fileSystemFilter,
private SkippedPathsResolver $skippedPathsResolver,
private Configuration $configuration,
private Cache $cache
) {
}
@ -47,36 +43,6 @@ final class FilesFinder
* @return SmartFileInfo[]
*/
public function findInDirectoriesAndFiles(array $source, array $suffixes): array
{
$cacheKey = md5(serialize($source) . serialize($suffixes));
if (! $this->configuration->isCacheEnabled() || $this->configuration->shouldClearCache()) {
$this->cache->clean([
Cache::ALL => true,
]);
return $this->collectFileInfos($source, $suffixes);
}
$loadCache = $this->cache->load($cacheKey);
if ($loadCache) {
$stringFiles = unserialize($loadCache);
return $this->getSmartFileInfosFromStringFiles($stringFiles);
}
$smartFileInfos = $this->collectFileInfos($source, $suffixes);
$stringFiles = serialize($this->convertFileInfosToStringFiles($smartFileInfos));
$this->cache->save($cacheKey, $stringFiles);
return $smartFileInfos;
}
/**
* @param string[] $source
* @param string[] $suffixes
* @return SmartFileInfo[]
*/
private function collectFileInfos(array $source, array $suffixes): array
{
$filesAndDirectories = $this->filesystemTweaker->resolveWithFnmatch($source);
@ -91,34 +57,6 @@ final class FilesFinder
return array_merge($smartFileInfos, $this->findInDirectories($directories, $suffixes));
}
/**
* @param SmartFileInfo[] $smartFileInfos
* @return string[]
*/
private function convertFileInfosToStringFiles(array $smartFileInfos): array
{
$files = [];
foreach ($smartFileInfos as $smartFileInfo) {
$files[] = $smartFileInfo->getPathname();
}
return $files;
}
/**
* @param string[] $files
* @return SmartFileInfo[]
*/
private function getSmartFileInfosFromStringFiles(array $files): array
{
$smartFileInfos = [];
foreach ($files as $file) {
$smartFileInfos[] = new SmartFileInfo($file);
}
return $smartFileInfos;
}
/**
* @param string[] $directories
* @param string[] $suffixes