mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-19 14:27:14 +01:00
Merge pull request #612 from rectorphp/finder-match
FilesFinder - allow searching in asterisk
This commit is contained in:
commit
b78fc0eacf
1
.gitignore
vendored
1
.gitignore
vendored
@ -7,3 +7,4 @@
|
||||
composer.lock
|
||||
|
||||
/demo
|
||||
rector-symfony.yml
|
@ -42,4 +42,4 @@ cache:
|
||||
- $HOME/.composer/cache
|
||||
|
||||
notifications:
|
||||
email: never
|
||||
email: false
|
||||
|
@ -107,7 +107,13 @@ Do you need to upgrade to **Symfony 4.0**, for example?
|
||||
vendor/bin/rector process src --level symfony33 --dry-run
|
||||
```
|
||||
|
||||
3. What levels are on the board?
|
||||
3. To process just specific subdirectories, you can use [fnmatch](http://php.net/manual/en/function.fnmatch.php) pattern with `*`:
|
||||
|
||||
```bash
|
||||
vendor/bin/rector process "src/Symfony/Component/*/Tests" --level phpunit60 --dry-run
|
||||
```
|
||||
|
||||
4. What levels are on the board?
|
||||
|
||||
```bash
|
||||
vendor/bin/rector levels
|
||||
|
@ -5,19 +5,22 @@ use Symfony\Component\Console\Input\ArgvInput;
|
||||
use Symplify\PackageBuilder\Configuration\ConfigFileFinder;
|
||||
use Symplify\PackageBuilder\Configuration\LevelFileFinder;
|
||||
|
||||
// 1. Detect configuration from --level
|
||||
$configFile = (new LevelFileFinder())->detectFromInputAndDirectory(new ArgvInput(), __DIR__ . '/../config/level');
|
||||
$configFiles = [];
|
||||
|
||||
// 2. Or from --config
|
||||
if ($configFile === null) {
|
||||
// Detect configuration from --level
|
||||
$configFiles[] = (new LevelFileFinder())->detectFromInputAndDirectory(new ArgvInput(), __DIR__ . '/../config/level');
|
||||
|
||||
// And from --config or default one
|
||||
ConfigFileFinder::detectFromInput('rector', new ArgvInput());
|
||||
$configFile = ConfigFileFinder::provide('rector', ['rector.yml', 'rector.yaml']);
|
||||
}
|
||||
$configFiles[] = ConfigFileFinder::provide('rector', ['rector.yml', 'rector.yaml']);
|
||||
|
||||
// remove empty values
|
||||
$configFiles = array_filter($configFiles);
|
||||
|
||||
// 3. Build DI container
|
||||
$containerFactory = new ContainerFactory();
|
||||
if ($configFile) {
|
||||
return $containerFactory->createWithConfig($configFile);
|
||||
if ($configFiles) {
|
||||
return $containerFactory->createWithConfigFiles($configFiles);
|
||||
}
|
||||
|
||||
return $containerFactory->create();
|
||||
|
@ -25,7 +25,7 @@
|
||||
},
|
||||
"require-dev": {
|
||||
"humbug/php-scoper": "^0.9.1",
|
||||
"phpunit/phpunit": "^7.1",
|
||||
"phpunit/phpunit": "^7.3",
|
||||
"slam/php-cs-fixer-extensions": "^1.15",
|
||||
"symplify/easy-coding-standard": "^4.6.1",
|
||||
"tracy/tracy": "^2.5"
|
||||
|
@ -23,4 +23,4 @@ after_script:
|
||||
fi
|
||||
|
||||
notifications:
|
||||
email: never
|
||||
email: false
|
||||
|
@ -12,10 +12,11 @@
|
||||
"symfony/dependency-injection": "^3.4|^4.0",
|
||||
"symfony/finder": "^3.4|^4.0",
|
||||
"symplify/better-phpdoc-parser": "^4.7",
|
||||
"rector/utils": "dev-master",
|
||||
"symplify/package-builder": "^4.7"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7.2"
|
||||
"phpunit/phpunit": "^7.3"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
@ -4,4 +4,7 @@ imports:
|
||||
# to cover installed as dependency
|
||||
- { resource: '../../../../../../symplify/better-phpdoc-parser/src/config/services.yml' , ignore_errors: true }
|
||||
|
||||
# rector/utils package
|
||||
- { resource: '../../../Utils/src/config/services.yml' }
|
||||
|
||||
- { resource: 'services.yml' }
|
||||
|
@ -6,6 +6,7 @@ use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\FuncCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\Rector\AbstractRector;
|
||||
@ -38,6 +39,14 @@ final class DefineConstantToStaticCallRector extends AbstractRector
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $node->name instanceof Name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($node->name->toString() !== 'defined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$argumentValue = $node->args[0]->value;
|
||||
if (! $argumentValue instanceof String_) {
|
||||
return null;
|
||||
|
@ -82,6 +82,7 @@ CODE_SAMPLE
|
||||
if ($parentClassName !== $this->controllerClass) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->chainMethodCallAnalyzer->isTypeAndChainCalls(
|
||||
$node,
|
||||
'Symfony\Component\HttpFoundation\Request',
|
||||
|
70
packages/Utils/src/FilesystemTweaker.php
Normal file
70
packages/Utils/src/FilesystemTweaker.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\Utils;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Exception\FileSystem\DirectoryNotFoundException;
|
||||
use Rector\Exception\FileSystem\FileNotFoundException;
|
||||
|
||||
final class FilesystemTweaker
|
||||
{
|
||||
/**
|
||||
* @param string[] $source
|
||||
* @return string[][]
|
||||
*/
|
||||
public function splitSourceToDirectoriesAndFiles(array $source): array
|
||||
{
|
||||
$files = [];
|
||||
$directories = [];
|
||||
|
||||
foreach ($source as $singleSource) {
|
||||
if (is_file($singleSource)) {
|
||||
$this->ensureFileExists($singleSource);
|
||||
$files[] = $singleSource;
|
||||
} else {
|
||||
$directories[] = $singleSource;
|
||||
}
|
||||
}
|
||||
|
||||
return [$files, $directories];
|
||||
}
|
||||
|
||||
/**
|
||||
* This will turn paths like "src/Symfony/Component/*\/Tests" to existing directory paths
|
||||
*
|
||||
* @param string[] $directories
|
||||
* @return string[]
|
||||
*/
|
||||
public function resolveDirectoriesWithFnmatch(array $directories): array
|
||||
{
|
||||
$absoluteDirectories = [];
|
||||
foreach ($directories as $directory) {
|
||||
if (Strings::contains($directory, '*')) { // is fnmatch for directories
|
||||
$absoluteDirectories = array_merge($absoluteDirectories, glob($directory, GLOB_ONLYDIR));
|
||||
} else { // is classic directory
|
||||
$this->ensureDirectoryExists($directory);
|
||||
$absoluteDirectories[] = $directory;
|
||||
}
|
||||
}
|
||||
|
||||
return $absoluteDirectories;
|
||||
}
|
||||
|
||||
private function ensureFileExists(string $file): void
|
||||
{
|
||||
if (file_exists($file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new FileNotFoundException(sprintf('File "%s" was not found.', $file));
|
||||
}
|
||||
|
||||
private function ensureDirectoryExists(string $directory): void
|
||||
{
|
||||
if (file_exists($directory)) {
|
||||
return;
|
||||
}
|
||||
|
||||
throw new DirectoryNotFoundException(sprintf('Directory "%s" was not found.', $directory));
|
||||
}
|
||||
}
|
@ -32,7 +32,7 @@ abstract class AbstractYamlRectorTest extends TestCase
|
||||
$this->fileGuard = new FileGuard();
|
||||
$this->fileGuard->ensureFileExists($config, get_called_class());
|
||||
|
||||
$this->container = (new ContainerFactory())->createWithConfig($config);
|
||||
$this->container = (new ContainerFactory())->createWithConfigFiles([$config]);
|
||||
|
||||
$this->yamlFileProcessor = $this->container->get(YamlFileProcessor::class);
|
||||
}
|
||||
|
@ -5,8 +5,12 @@ namespace Rector\Autoloading;
|
||||
use Nette\Loaders\RobotLoader;
|
||||
use Rector\Configuration\Option;
|
||||
use Rector\FileSystem\FileGuard;
|
||||
use Rector\Utils\FilesystemTweaker;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
|
||||
/**
|
||||
* Should it pass autoload files/directories to PHPStan analyzer?
|
||||
*/
|
||||
final class AdditionalAutoloader
|
||||
{
|
||||
/**
|
||||
@ -24,21 +28,43 @@ final class AdditionalAutoloader
|
||||
*/
|
||||
private $autoloadDirectories = [];
|
||||
|
||||
/**
|
||||
* @var FilesystemTweaker
|
||||
*/
|
||||
private $filesystemTweaker;
|
||||
|
||||
/**
|
||||
* @param string[] $autoloadFiles
|
||||
* @param string[] $autoloadDirectories
|
||||
*/
|
||||
public function __construct(array $autoloadFiles, array $autoloadDirectories)
|
||||
{
|
||||
public function __construct(
|
||||
array $autoloadFiles,
|
||||
array $autoloadDirectories,
|
||||
FileGuard $fileGuard,
|
||||
FilesystemTweaker $filesystemTweaker
|
||||
) {
|
||||
$this->autoloadFiles = $autoloadFiles;
|
||||
$this->autoloadDirectories = $autoloadDirectories;
|
||||
$this->fileGuard = $fileGuard;
|
||||
$this->filesystemTweaker = $filesystemTweaker;
|
||||
}
|
||||
|
||||
public function autoloadWithInput(InputInterface $input): void
|
||||
/**
|
||||
* @param string[] $source
|
||||
*/
|
||||
public function autoloadWithInputAndSource(InputInterface $input, array $source): void
|
||||
{
|
||||
$this->autoloadFileFromInput($input);
|
||||
$this->autoloadDirectories($this->autoloadDirectories);
|
||||
$this->autoloadFiles($this->autoloadFiles);
|
||||
|
||||
[$files, $directories] = $this->filesystemTweaker->splitSourceToDirectoriesAndFiles($source);
|
||||
$this->autoloadFiles($files);
|
||||
|
||||
$absoluteDirectories = $this->filesystemTweaker->resolveDirectoriesWithFnmatch($directories);
|
||||
if (count($absoluteDirectories)) {
|
||||
$this->autoloadDirectories($absoluteDirectories);
|
||||
}
|
||||
}
|
||||
|
||||
private function autoloadFileFromInput(InputInterface $input): void
|
||||
|
@ -138,8 +138,6 @@ final class ProcessCommand extends Command
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$this->additionalAutoloader->autoloadWithInput($input);
|
||||
|
||||
$this->rectorGuard->ensureSomeRectorsAreRegistered();
|
||||
|
||||
$source = $input->getArgument(Option::SOURCE);
|
||||
@ -150,6 +148,8 @@ final class ProcessCommand extends Command
|
||||
$yamlFileInfos = $this->filesFinder->findInDirectoriesAndFiles($source, ['yml', 'yaml']);
|
||||
$allFileInfos = $phpFileInfos + $yamlFileInfos;
|
||||
|
||||
$this->additionalAutoloader->autoloadWithInputAndSource($input, $source);
|
||||
|
||||
$this->processCommandReporter->reportLoadedRectors();
|
||||
|
||||
$this->processFileInfos($allFileInfos);
|
||||
|
@ -16,9 +16,12 @@ final class ContainerFactory
|
||||
return $appKernel->getContainer();
|
||||
}
|
||||
|
||||
public function createWithConfig(string $config): ContainerInterface
|
||||
/**
|
||||
* @param string[] $configFiles
|
||||
*/
|
||||
public function createWithConfigFiles(array $configFiles): ContainerInterface
|
||||
{
|
||||
$appKernel = new RectorKernel($config);
|
||||
$appKernel = new RectorKernel($configFiles);
|
||||
$appKernel->boot();
|
||||
// this is require to keep CLI verbosity independent on AppKernel dev/prod mode
|
||||
putenv('SHELL_VERBOSITY=0');
|
||||
|
@ -24,26 +24,29 @@ use Symplify\PackageBuilder\Yaml\FileLoader\ParameterImportsYamlFileLoader;
|
||||
final class RectorKernel extends Kernel
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
* @var string[]
|
||||
*/
|
||||
private $configFile;
|
||||
private $extraConfigFiles = [];
|
||||
|
||||
public function __construct(?string $configFile = '')
|
||||
/**
|
||||
* @param string[] $configFiles
|
||||
*/
|
||||
public function __construct(array $configFiles = [])
|
||||
{
|
||||
if ($configFile) {
|
||||
$this->configFile = $configFile;
|
||||
}
|
||||
$this->extraConfigFiles = $configFiles;
|
||||
|
||||
// debug: true is require to invalidate container on service files change
|
||||
parent::__construct('cli' . sha1((string) $configFile), true);
|
||||
$configFilesHash = md5(serialize($configFiles));
|
||||
|
||||
// debug: require to invalidate container on service files change
|
||||
parent::__construct('cli_' . $configFilesHash, true);
|
||||
}
|
||||
|
||||
public function registerContainerConfiguration(LoaderInterface $loader): void
|
||||
{
|
||||
$loader->load(__DIR__ . '/../config/config.yml');
|
||||
|
||||
if ($this->configFile) {
|
||||
$loader->load($this->configFile);
|
||||
foreach ($this->extraConfigFiles as $extraConfigFile) {
|
||||
$loader->load($extraConfigFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,8 @@
|
||||
namespace Rector\FileSystem;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Exception\FileSystem\DirectoryNotFoundException;
|
||||
use Rector\Utils\FilesystemTweaker;
|
||||
use SplFileInfo as NativeSplFileInfo;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symfony\Component\Finder\SplFileInfo;
|
||||
|
||||
@ -19,12 +20,18 @@ final class FilesFinder
|
||||
*/
|
||||
private $excludePaths = [];
|
||||
|
||||
/**
|
||||
* @var FilesystemTweaker
|
||||
*/
|
||||
private $filesystemTweaker;
|
||||
|
||||
/**
|
||||
* @param string[] $excludePaths
|
||||
*/
|
||||
public function __construct(array $excludePaths)
|
||||
public function __construct(array $excludePaths, FilesystemTweaker $filesystemTweaker)
|
||||
{
|
||||
$this->excludePaths = $excludePaths;
|
||||
$this->filesystemTweaker = $filesystemTweaker;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,22 +46,16 @@ final class FilesFinder
|
||||
return $this->fileInfosBySourceAndSuffixes[$cacheKey];
|
||||
}
|
||||
|
||||
$files = [];
|
||||
$directories = [];
|
||||
[$files, $directories] = $this->filesystemTweaker->splitSourceToDirectoriesAndFiles($source);
|
||||
|
||||
foreach ($source as $singleSource) {
|
||||
if (is_file($singleSource)) {
|
||||
$files[] = new SplFileInfo($singleSource, '', '');
|
||||
} else {
|
||||
$directories[] = $singleSource;
|
||||
}
|
||||
$splFileInfos = [];
|
||||
foreach ($files as $file) {
|
||||
$splFileInfos[] = new SplFileInfo($file, '', '');
|
||||
}
|
||||
|
||||
if (count($directories)) {
|
||||
$files = array_merge($files, $this->findInDirectories($directories, $suffixes));
|
||||
}
|
||||
$splFileInfos = array_merge($splFileInfos, $this->findInDirectories($directories, $suffixes));
|
||||
|
||||
return $this->fileInfosBySourceAndSuffixes[$cacheKey] = $files;
|
||||
return $this->fileInfosBySourceAndSuffixes[$cacheKey] = $splFileInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -64,39 +65,27 @@ final class FilesFinder
|
||||
*/
|
||||
private function findInDirectories(array $directories, array $suffixes): array
|
||||
{
|
||||
$this->ensureDirectoriesExist($directories);
|
||||
if (! count($directories)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$absoluteDirectories = $this->filesystemTweaker->resolveDirectoriesWithFnmatch($directories);
|
||||
if (! $absoluteDirectories) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$suffixesPattern = $this->normalizeSuffixesToPattern($suffixes);
|
||||
|
||||
$finder = Finder::create()
|
||||
->files()
|
||||
->in($absoluteDirectories)
|
||||
->name($suffixesPattern)
|
||||
->in($directories)
|
||||
->exclude(['examples', 'Examples', 'stubs', 'Stubs', 'fixtures', 'Fixtures', 'polyfill', 'Polyfill'])
|
||||
->notName('*polyfill*');
|
||||
|
||||
$splFileInfos = iterator_to_array($finder->getIterator());
|
||||
if (! $this->excludePaths) {
|
||||
return $splFileInfos;
|
||||
}
|
||||
$this->addFilterWithExcludedPaths($finder);
|
||||
|
||||
// to overcome magic behavior: https://github.com/symfony/symfony/pull/26396/files
|
||||
/** @var SplFileInfo[] $splFileInfos */
|
||||
return $this->filterOutFilesByPatterns($splFileInfos, $this->excludePaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $directories
|
||||
*/
|
||||
private function ensureDirectoriesExist(array $directories): void
|
||||
{
|
||||
foreach ($directories as $directory) {
|
||||
if (file_exists($directory)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
throw new DirectoryNotFoundException(sprintf('Directory "%s" was not found.', $directory));
|
||||
}
|
||||
return iterator_to_array($finder->getIterator());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -109,29 +98,22 @@ final class FilesFinder
|
||||
return '#\.(' . $suffixesPattern . ')$#';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SplFileInfo[] $splFileInfos
|
||||
* @param string[] $patternsToExclude
|
||||
* @return SplFileInfo[]
|
||||
*/
|
||||
private function filterOutFilesByPatterns(array $splFileInfos, array $patternsToExclude): array
|
||||
private function addFilterWithExcludedPaths(Finder $finder): void
|
||||
{
|
||||
$filteredFiles = [];
|
||||
|
||||
foreach ($splFileInfos as $relativePath => $splFileInfo) {
|
||||
foreach ($patternsToExclude as $patternToExclude) {
|
||||
if (Strings::match($splFileInfo->getRealPath(), $patternToExclude)) {
|
||||
continue;
|
||||
if (! $this->excludePaths) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (fnmatch($splFileInfo->getRealPath(), $patternToExclude)) {
|
||||
continue;
|
||||
$finder->filter(function (NativeSplFileInfo $splFileInfo) {
|
||||
foreach ($this->excludePaths as $excludePath) {
|
||||
if (Strings::match($splFileInfo->getRealPath(), $excludePath)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$filteredFiles[$relativePath] = $splFileInfo;
|
||||
}
|
||||
return fnmatch($splFileInfo->getRealPath(), $excludePath);
|
||||
}
|
||||
|
||||
return $filteredFiles;
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -44,16 +44,18 @@ abstract class AbstractRectorTestCase extends TestCase
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$config = $this->provideConfig();
|
||||
$configFile = $this->provideConfig();
|
||||
$this->fileGuard = new FileGuard();
|
||||
$this->fileGuard->ensureFileExists($config, get_called_class());
|
||||
$this->fileGuard->ensureFileExists($configFile, get_called_class());
|
||||
|
||||
$key = md5_file($config);
|
||||
$key = md5_file($configFile);
|
||||
|
||||
if (isset(self::$containersPerConfig[$key]) && $this->rebuildFreshContainer === false) {
|
||||
$this->container = self::$containersPerConfig[$key];
|
||||
} else {
|
||||
self::$containersPerConfig[$key] = $this->container = (new ContainerFactory())->createWithConfig($config);
|
||||
self::$containersPerConfig[$key] = $this->container = (new ContainerFactory())->createWithConfigFiles(
|
||||
[$configFile]
|
||||
);
|
||||
}
|
||||
|
||||
$this->fileProcessor = $this->container->get(FileProcessor::class);
|
||||
|
@ -20,7 +20,7 @@ abstract class AbstractConfigurableContainerAwareTestCase extends TestCase
|
||||
*/
|
||||
public function __construct(?string $name = null, array $data = [], string $dataName = '')
|
||||
{
|
||||
$this->container = (new ContainerFactory())->createWithConfig($this->provideConfig());
|
||||
$this->container = (new ContainerFactory())->createWithConfigFiles([$this->provideConfig()]);
|
||||
|
||||
parent::__construct($name, $data, $dataName);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user