allow loading --config and --level together [closes #613]

This commit is contained in:
Tomas Votruba 2018-08-30 11:33:52 +02:00
parent d114f0649c
commit 1c51f5d9bf
14 changed files with 131 additions and 59 deletions

3
.gitignore vendored
View File

@ -6,4 +6,5 @@
/build
composer.lock
/demo
/demo
rector-symfony.yml

View File

@ -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) {
ConfigFileFinder::detectFromInput('rector', new ArgvInput());
$configFile = ConfigFileFinder::provide('rector', ['rector.yml', 'rector.yaml']);
}
// Detect configuration from --level
$configFiles[] = (new LevelFileFinder())->detectFromInputAndDirectory(new ArgvInput(), __DIR__ . '/../config/level');
// And from --config or default one
ConfigFileFinder::detectFromInput('rector', new ArgvInput());
$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();

View File

@ -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"

View File

@ -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;

View File

@ -82,6 +82,7 @@ CODE_SAMPLE
if ($parentClassName !== $this->controllerClass) {
return null;
}
if (! $this->chainMethodCallAnalyzer->isTypeAndChainCalls(
$node,
'Symfony\Component\HttpFoundation\Request',

View File

@ -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);
}

View File

@ -47,10 +47,6 @@ parameters:
- '#Parameter \#1 \$pattern of function fnmatch expects string, string\|false given#'
- '#Parameter \#1 \$file of function file_put_contents expects string, string\|false given#'
- '#Parameter \#1 \$filePath of method Rector\\Parser\\Parser::parseFile\(\) expects string, string\|false given#'
- '#Parameter \#2 \$filename of function fnmatch expects string, string\|false given#'
# getcwd false postive
- '#Parameter \#1 \$dirs of method Symfony\\Component\\Finder\\Finder::in\(\) expects array\|string, string\|false given#'
# false positive, has annotation type above (@todo recheck for possible ignored positives)
- '#Access to an undefined property PhpParser\\Node::\$name#' # 11

View File

@ -3,10 +3,14 @@
namespace Rector\Autoloading;
use Nette\Loaders\RobotLoader;
use Nette\Utils\Strings;
use Rector\Configuration\Option;
use Rector\FileSystem\FileGuard;
use Symfony\Component\Console\Input\InputInterface;
/**
* Should it pass autoload files/directories to PHPStan analyzer?
*/
final class AdditionalAutoloader
{
/**
@ -28,10 +32,11 @@ final class AdditionalAutoloader
* @param string[] $autoloadFiles
* @param string[] $autoloadDirectories
*/
public function __construct(array $autoloadFiles, array $autoloadDirectories)
public function __construct(array $autoloadFiles, array $autoloadDirectories, FileGuard $fileGuard)
{
$this->autoloadFiles = $autoloadFiles;
$this->autoloadDirectories = $autoloadDirectories;
$this->fileGuard = $fileGuard;
}
public function autoloadWithInput(InputInterface $input): void
@ -41,6 +46,32 @@ final class AdditionalAutoloader
$this->autoloadFiles($this->autoloadFiles);
}
/**
* @param string[] $source
*/
public function autoloadWithInputAndSource(InputInterface $input, array $source): void
{
$this->autoloadWithInput($input);
[$files, $directories] = $this->splitSourceToDirectoriesAndFiles($source);
// @todo include the files so people don't have to do it manually?
$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
$absoluteDirectories[] = $directory;
}
}
if (count($absoluteDirectories)) {
$this->autoloadDirectories($absoluteDirectories);
}
}
private function autoloadFileFromInput(InputInterface $input): void
{
/** @var string|null $autoloadFile */
@ -52,6 +83,27 @@ final class AdditionalAutoloader
$this->autoloadFiles([$autoloadFile]);
}
/**
* @todo decouple to standalone Finder/File something
* @param string[] $source
* @return string[][]|string[]
*/
private function splitSourceToDirectoriesAndFiles(array $source): array
{
$files = [];
$directories = [];
foreach ($source as $singleSource) {
if (is_file($singleSource)) {
$files[] = $singleSource;
} else {
$directories[] = $singleSource;
}
}
return [$files, $directories];
}
/**
* @param string[] $directories
*/

View File

@ -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);

View File

@ -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');

View File

@ -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);
}
}

View File

@ -56,6 +56,10 @@ final class FilesFinder
private function findInDirectories(array $directories, array $suffixes): array
{
$absoluteDirectories = $this->resolveAbsoluteDirectories($directories);
if (! $absoluteDirectories) {
return [];
}
$suffixesPattern = $this->normalizeSuffixesToPattern($suffixes);
$finder = Finder::create()
@ -65,17 +69,7 @@ final class FilesFinder
->exclude(['examples', 'Examples', 'stubs', 'Stubs', 'fixtures', 'Fixtures', 'polyfill', 'Polyfill'])
->notName('*polyfill*');
if ($this->excludePaths) {
$finder->filter(function (NativeSplFileInfo $splFileInfo) {
foreach ($this->excludePaths as $excludePath) {
if (Strings::match($splFileInfo->getRealPath(), $excludePath)) {
return true;
}
return fnmatch($splFileInfo->getRealPath(), $excludePath);
}
});
}
$this->addFilterWithExcludedPaths($finder);
return iterator_to_array($finder->getIterator());
}
@ -140,18 +134,7 @@ final class FilesFinder
foreach ($directories as $directory) {
if (Strings::contains($directory, '*')) { // is fnmatch for directories
$directoriesFinder = Finder::create()
->in(getcwd())
->directories()
->filter(function (NativeSplFileInfo $splFileInfo) use ($directory) {
// keep only file that match specific pattern
return fnmatch('*' . $directory . '*', $splFileInfo->getRealPath());
});
$absoluteDirectories = array_merge(
$absoluteDirectories,
iterator_to_array($directoriesFinder->getIterator())
);
$absoluteDirectories = array_merge($absoluteDirectories, glob($directory, GLOB_ONLYDIR));
} else { // is classic directory
$this->ensureDirectoryExists($directory);
$absoluteDirectories[] = $directory;
@ -160,4 +143,23 @@ final class FilesFinder
return $absoluteDirectories;
}
private function addFilterWithExcludedPaths(Finder $finder): void
{
if (! $this->excludePaths) {
return;
}
$finder->filter(function (NativeSplFileInfo $splFileInfo) {
foreach ($this->excludePaths as $excludePath) {
if (Strings::match($splFileInfo->getRealPath(), $excludePath)) {
return true;
}
return fnmatch($splFileInfo->getRealPath(), $excludePath);
}
return false;
});
}
}

View File

@ -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);

View File

@ -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);
}