mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-31 08:20:53 +00:00
move from nette/caching to own cache based on PHPStan (#202)
This commit is contained in:
parent
9dea8d8cd8
commit
ac81e98fbf
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
41
packages/Caching/Cache.php
Normal file
41
packages/Caching/Cache.php
Normal 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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
26
packages/Caching/Enum/CacheKey.php
Normal file
26
packages/Caching/Enum/CacheKey.php
Normal 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';
|
||||
}
|
30
packages/Caching/ValueObject/CacheFilePaths.php
Normal file
30
packages/Caching/ValueObject/CacheFilePaths.php
Normal 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;
|
||||
}
|
||||
}
|
42
packages/Caching/ValueObject/CacheItem.php
Normal file
42
packages/Caching/ValueObject/CacheItem.php
Normal 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;
|
||||
}
|
||||
}
|
107
packages/Caching/ValueObject/Storage/FileCacheStorage.php
Normal file
107
packages/Caching/ValueObject/Storage/FileCacheStorage.php
Normal 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);
|
||||
}
|
||||
}
|
13
phpstan.neon
13
phpstan.neon
|
@ -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#'
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue
Block a user