Recipe array to object (#3966)

Co-authored-by: rector-bot <tomas@getrector.org>
This commit is contained in:
Tomas Votruba 2020-08-16 00:49:15 +02:00 committed by GitHub
parent 2d7469efb8
commit 664388561d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 536 additions and 698 deletions

View File

@ -2,7 +2,6 @@
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\RectorGenerator\Rector\Closure\AddNewServiceToSymfonyPhpConfigRector;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\ref;
@ -28,8 +27,4 @@ return static function (ContainerConfigurator $containerConfigurator): void {
->autowire(false);
$services->set(FileSystemGuard::class);
$parameters = $containerConfigurator->parameters();
$parameters->set(Option::RECTOR_RECIPE, []);
};

View File

@ -5,14 +5,13 @@ declare(strict_types=1);
namespace Rector\RectorGenerator\Command;
use Nette\Utils\Strings;
use Rector\Core\Configuration\Option;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\RectorGenerator\Composer\ComposerPackageAutoloadUpdater;
use Rector\RectorGenerator\Config\ConfigFilesystem;
use Rector\RectorGenerator\Configuration\ConfigurationFactory;
use Rector\RectorGenerator\Finder\TemplateFinder;
use Rector\RectorGenerator\Generator\FileGenerator;
use Rector\RectorGenerator\Guard\OverrideGuard;
use Rector\RectorGenerator\Provider\RectorRecipeProvider;
use Rector\RectorGenerator\TemplateVariablesFactory;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
@ -20,21 +19,15 @@ use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\PackageBuilder\Console\Command\CommandNaming;
use Symplify\PackageBuilder\Console\ShellCode;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
use Symplify\SmartFileSystem\SmartFileInfo;
final class CreateCommand extends Command
final class GenerateCommand extends Command
{
/**
* @var SymfonyStyle
*/
private $symfonyStyle;
/**
* @var ConfigurationFactory
*/
private $configurationFactory;
/**
* @var TemplateVariablesFactory
*/
@ -60,71 +53,62 @@ final class CreateCommand extends Command
*/
private $overrideGuard;
/**
* @var ParameterProvider
*/
private $parameterProvider;
/**
* @var FileGenerator
*/
private $fileGenerator;
/**
* @var RectorRecipeProvider
*/
private $rectorRecipeProvider;
public function __construct(
ComposerPackageAutoloadUpdater $composerPackageAutoloadUpdater,
ConfigFilesystem $configFilesystem,
ConfigurationFactory $configurationFactory,
FileGenerator $fileGenerator,
OverrideGuard $overrideGuard,
ParameterProvider $parameterProvider,
SymfonyStyle $symfonyStyle,
TemplateFinder $templateFinder,
TemplateVariablesFactory $templateVariablesFactory
TemplateVariablesFactory $templateVariablesFactory,
RectorRecipeProvider $rectorRecipeProvider
) {
parent::__construct();
$this->symfonyStyle = $symfonyStyle;
$this->configurationFactory = $configurationFactory;
$this->templateVariablesFactory = $templateVariablesFactory;
$this->composerPackageAutoloadUpdater = $composerPackageAutoloadUpdater;
$this->templateFinder = $templateFinder;
$this->configFilesystem = $configFilesystem;
$this->overrideGuard = $overrideGuard;
$this->parameterProvider = $parameterProvider;
$this->fileGenerator = $fileGenerator;
$this->rectorRecipeProvider = $rectorRecipeProvider;
}
protected function configure(): void
{
$this->setName(CommandNaming::classToName(self::class));
$this->setAliases(['c', 'generate', 'g']);
$this->setAliases(['c', 'create', 'g']);
$this->setDescription('[DEV] Create a new Rector, in a proper location, with new tests');
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$rectorRecipe = $this->parameterProvider->provideParameter(Option::RECTOR_RECIPE);
$rectorRecipe = $this->rectorRecipeProvider->provide();
$isRectorRepository = $this->isRectorRepository();
$rectorRecipeConfiguration = $this->configurationFactory->createFromRectorRecipe(
$rectorRecipe,
$isRectorRepository
);
$templateVariables = $this->templateVariablesFactory->createFromConfiguration($rectorRecipeConfiguration);
$templateVariables = $this->templateVariablesFactory->createFromRectorRecipe($rectorRecipe);
// setup psr-4 autoload, if not already in
$this->composerPackageAutoloadUpdater->processComposerAutoload($rectorRecipeConfiguration);
$this->composerPackageAutoloadUpdater->processComposerAutoload($rectorRecipe);
$templateFileInfos = $this->templateFinder->find($rectorRecipeConfiguration);
$templateFileInfos = $this->templateFinder->find($rectorRecipe);
$targetDirectory = getcwd();
$isUnwantedOverride = $this->overrideGuard->isUnwantedOverride(
$templateFileInfos,
$templateVariables,
$rectorRecipeConfiguration,
$rectorRecipe,
$targetDirectory
);
if ($isUnwantedOverride) {
@ -136,24 +120,19 @@ final class CreateCommand extends Command
$generatedFilePaths = $this->fileGenerator->generateFiles(
$templateFileInfos,
$templateVariables,
$rectorRecipeConfiguration,
$rectorRecipe,
$targetDirectory
);
$testCaseDirectoryPath = $this->resolveTestCaseDirectoryPath($generatedFilePaths);
$this->configFilesystem->appendRectorServiceToSet($rectorRecipeConfiguration, $templateVariables);
$this->configFilesystem->appendRectorServiceToSet($rectorRecipe, $templateVariables);
$this->printSuccess($rectorRecipeConfiguration->getName(), $generatedFilePaths, $testCaseDirectoryPath);
$this->printSuccess($rectorRecipe->getName(), $generatedFilePaths, $testCaseDirectoryPath);
return ShellCode::SUCCESS;
}
private function isRectorRepository(): bool
{
return file_exists(__DIR__ . '/../../../../vendor');
}
/**
* @param string[] $generatedFilePaths
*/

View File

@ -6,7 +6,7 @@ namespace Rector\RectorGenerator\Composer;
use Rector\RectorGenerator\FileSystem\JsonFileSystem;
use Rector\RectorGenerator\ValueObject\Package;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\Process;
@ -38,12 +38,12 @@ final class ComposerPackageAutoloadUpdater
$this->symfonyStyle = $symfonyStyle;
}
public function processComposerAutoload(RectorRecipeConfiguration $rectorRecipeConfiguration): void
public function processComposerAutoload(RectorRecipe $rectorRecipe): void
{
$composerJsonFilePath = getcwd() . '/composer.json';
$composerJson = $this->jsonFileSystem->loadFileToJson($composerJsonFilePath);
$package = $this->resolvePackage($rectorRecipeConfiguration);
$package = $this->resolvePackage($rectorRecipe);
if ($this->isPackageAlreadyLoaded($composerJson, $package)) {
return;
@ -60,7 +60,7 @@ final class ComposerPackageAutoloadUpdater
return;
}
$srcAutoload = $rectorRecipeConfiguration->isRectorRepository() ? 'autoload' : self::AUTOLOAD_DEV;
$srcAutoload = $rectorRecipe->isRectorRepository() ? 'autoload' : self::AUTOLOAD_DEV;
$composerJson[$srcAutoload][self::PSR_4][$package->getSrcNamespace()] = $package->getSrcDirectory();
$composerJson[self::AUTOLOAD_DEV][self::PSR_4][$package->getTestsNamespace()] = $package->getTestsDirectory();
@ -70,9 +70,9 @@ final class ComposerPackageAutoloadUpdater
$this->rebuildAutoload();
}
private function resolvePackage(RectorRecipeConfiguration $rectorRecipeConfiguration): Package
private function resolvePackage(RectorRecipe $rectorRecipe): Package
{
if (! $rectorRecipeConfiguration->isRectorRepository()) {
if (! $rectorRecipe->isRectorRepository()) {
return new Package(
'Utils\\Rector\\',
'Utils\\Rector\\Tests\\',
@ -82,10 +82,10 @@ final class ComposerPackageAutoloadUpdater
}
return new Package(
'Rector\\' . $rectorRecipeConfiguration->getPackage() . '\\',
'Rector\\' . $rectorRecipeConfiguration->getPackage() . '\\Tests\\',
'rules/' . $rectorRecipeConfiguration->getPackageDirectory() . '/src',
'rules/' . $rectorRecipeConfiguration->getPackageDirectory() . '/tests'
'Rector\\' . $rectorRecipe->getPackage() . '\\',
'Rector\\' . $rectorRecipe->getPackage() . '\\Tests\\',
'rules/' . $rectorRecipe->getPackageDirectory() . '/src',
'rules/' . $rectorRecipe->getPackageDirectory() . '/tests'
);
}

View File

@ -12,7 +12,8 @@ use Rector\Core\PhpParser\Parser\Parser;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\RectorGenerator\Rector\Closure\AddNewServiceToSymfonyPhpConfigRector;
use Rector\RectorGenerator\TemplateFactory;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Symplify\SmartFileSystem\SmartFileInfo;
use Symplify\SmartFileSystem\SmartFileSystem;
final class ConfigFilesystem
@ -64,16 +65,15 @@ final class ConfigFilesystem
/**
* @param string[] $templateVariables
*/
public function appendRectorServiceToSet(
RectorRecipeConfiguration $rectorRecipeConfiguration,
array $templateVariables
): void {
if ($rectorRecipeConfiguration->getSetConfig() === null) {
public function appendRectorServiceToSet(RectorRecipe $rectorRecipe, array $templateVariables): void
{
if ($rectorRecipe->getSet() === null) {
return;
}
$setConfigFileInfo = $rectorRecipeConfiguration->getSetConfig();
$setFileContents = $setConfigFileInfo->getContents();
$setFilePath = $rectorRecipe->getSet();
$setFileInfo = new SmartFileInfo($setFilePath);
$setFileContents = $setFileInfo->getContents();
// already added?
$rectorFqnName = $this->templateFactory->create(self::RECTOR_FQN_NAME_PATTERN, $templateVariables);
@ -82,7 +82,7 @@ final class ConfigFilesystem
}
// 1. parse the file
$setConfigNodes = $this->parser->parseFileInfo($setConfigFileInfo);
$setConfigNodes = $this->parser->parseFileInfo($setFileInfo);
// 2. add the set() call
$this->decorateNamesToFullyQualified($setConfigNodes);
@ -95,7 +95,7 @@ final class ConfigFilesystem
// 3. print the content back to file
$changedSetConfigContent = $this->betterStandardPrinter->prettyPrintFile($setConfigNodes);
$this->smartFileSystem->dumpFile($setConfigFileInfo->getRealPath(), $changedSetConfigContent);
$this->smartFileSystem->dumpFile($setFileInfo->getRealPath(), $changedSetConfigContent);
}
/**

View File

@ -1,103 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\RectorGenerator\Configuration;
use Nette\Utils\Strings;
use Rector\Core\Set\SetResolver;
use Rector\RectorGenerator\Guard\RecipeGuard;
use Rector\RectorGenerator\ValueObject\RecipeOption;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
use Symplify\SetConfigResolver\ValueObject\Set;
final class ConfigurationFactory
{
/**
* @var RecipeGuard
*/
private $recipeGuard;
/**
* @var SetResolver
*/
private $setResolver;
public function __construct(RecipeGuard $recipeGuard, SetResolver $setResolver)
{
$this->recipeGuard = $recipeGuard;
$this->setResolver = $setResolver;
}
/**
* @param mixed[] $rectorRecipe
*/
public function createFromRectorRecipe(array $rectorRecipe, bool $isRectorRepository): RectorRecipeConfiguration
{
$this->recipeGuard->ensureRecipeIsValid($rectorRecipe, $isRectorRepository);
$nodeTypeClasses = $rectorRecipe[RecipeOption::NODE_TYPES];
$category = $this->resolveCategoryFromFqnNodeTypes($nodeTypeClasses);
$extraFileContent = $this->resolveExtraFileContent($rectorRecipe);
$set = $this->resolveeSet($rectorRecipe);
return new RectorRecipeConfiguration(
$rectorRecipe[RecipeOption::PACKAGE],
$rectorRecipe[RecipeOption::NAME],
$category,
$nodeTypeClasses,
$rectorRecipe[RecipeOption::DESCRIPTION],
$this->normalizeCode($rectorRecipe[RecipeOption::CODE_BEFORE]),
$this->normalizeCode($rectorRecipe[RecipeOption::CODE_AFTER]),
$extraFileContent,
$rectorRecipe[RecipeOption::EXTRA_FILE_NAME] ?? null,
(array) $rectorRecipe[RecipeOption::RULE_CONFIGURATION],
array_filter((array) $rectorRecipe[RecipeOption::SOURCE]),
$set,
$this->isPhpSnippet($rectorRecipe[RecipeOption::CODE_BEFORE]),
$isRectorRepository
);
}
/**
* Covered by PHPStan
* @see \Rector\PHPStanExtensions\Rule\RequireRectorCategoryByGetNodeTypesRule
*
* @param class-string[] $nodeTypes
*/
private function resolveCategoryFromFqnNodeTypes(array $nodeTypes): string
{
return (string) Strings::after($nodeTypes[0], '\\', -1);
}
private function resolveExtraFileContent(array $rectorRecipe): ?string
{
return isset($rectorRecipe[RecipeOption::EXTRA_FILE_CONTENT]) ? $this->normalizeCode(
$rectorRecipe[RecipeOption::EXTRA_FILE_CONTENT]
) : null;
}
private function resolveeSet(array $rectorRecipe): ?Set
{
if ($rectorRecipe[RecipeOption::SET]) {
return $this->setResolver->resolveSetByName($rectorRecipe[RecipeOption::SET]);
}
return null;
}
private function normalizeCode(string $code): string
{
if (Strings::startsWith($code, '<?php')) {
$code = ltrim($code, '<?php');
}
return trim($code);
}
private function isPhpSnippet(string $code): bool
{
return Strings::startsWith($code, '<?php');
}
}

View File

@ -7,8 +7,7 @@ namespace Rector\RectorGenerator\FileSystem;
use Nette\Utils\Strings;
use Rector\Core\Testing\PHPUnit\StaticPHPUnitEnvironment;
use Rector\RectorGenerator\Finder\TemplateFinder;
use Rector\RectorGenerator\ValueObject\Package;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Symplify\SmartFileSystem\SmartFileInfo;
final class TemplateFileSystem
@ -19,13 +18,13 @@ final class TemplateFileSystem
public function resolveDestination(
SmartFileInfo $smartFileInfo,
array $templateVariables,
RectorRecipeConfiguration $rectorRecipeConfiguration,
RectorRecipe $rectorRecipe,
string $targetDirectory
): string {
$destination = $smartFileInfo->getRelativeFilePathFromDirectory(TemplateFinder::TEMPLATES_DIRECTORY);
// normalize core package
if (! $rectorRecipeConfiguration->isRectorRepository()) {
if (! $rectorRecipe->isRectorRepository()) {
// special keyword for 3rd party Rectors, not for core Github contribution
$destination = Strings::replace($destination, '#(packages|rules)\/__package__#i', 'utils/rector');
}

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Rector\RectorGenerator\Finder;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Symplify\SmartFileSystem\FileSystemGuard;
use Symplify\SmartFileSystem\Finder\FinderSanitizer;
use Symplify\SmartFileSystem\SmartFileInfo;
@ -35,18 +35,18 @@ final class TemplateFinder
/**
* @return SmartFileInfo[]
*/
public function find(RectorRecipeConfiguration $rectorRecipeConfiguration): array
public function find(RectorRecipe $rectorRecipe): array
{
$filePaths = [];
if ($rectorRecipeConfiguration->getExtraFileContent()) {
if ($rectorRecipe->getExtraFileContent()) {
$filePaths[] = __DIR__ . '/../../templates/rules/__package__/tests/Rector/__Category__/__Name__/Source/extra_file.php.inc';
}
$filePaths = $this->addRuleAndTestCase($rectorRecipeConfiguration, $filePaths);
$filePaths = $this->addRuleAndTestCase($rectorRecipe, $filePaths);
/** @var string[] $filePaths */
$filePaths[] = $this->resolveFixtureFilePath($rectorRecipeConfiguration->isPhpSnippet());
$filePaths[] = $this->resolveFixtureFilePath($rectorRecipe->isPhpSnippet());
$this->ensureFilePathsExists($filePaths);
@ -60,12 +60,12 @@ final class TemplateFinder
* @note the ".inc" suffix is needed, so PHPUnit doens't load it as a test case;
* unfortunately we haven't found a way to preven it
*/
private function addRuleAndTestCase(RectorRecipeConfiguration $rectorRecipeConfiguration, array $filePaths): array
private function addRuleAndTestCase(RectorRecipe $rectorRecipe, array $filePaths): array
{
if ($rectorRecipeConfiguration->getRuleConfiguration() !== []) {
if ($rectorRecipe->getConfigration() !== []) {
$filePaths[] = __DIR__ . '/../../templates/rules/__package__/src/Rector/__Category__/__Configured__Name__.php';
if ($rectorRecipeConfiguration->getExtraFileContent()) {
if ($rectorRecipe->getExtraFileContent()) {
$filePaths[] = __DIR__ . '/../../templates/rules/__package__/tests/Rector/__Category__/__Name__/__Configured__Extra__Name__Test.php.inc';
} else {
$filePaths[] = __DIR__ . '/../../templates/rules/__package__/tests/Rector/__Category__/__Name__/__Configured__Name__Test.php.inc';
@ -74,7 +74,7 @@ final class TemplateFinder
return $filePaths;
}
if ($rectorRecipeConfiguration->getExtraFileContent()) {
if ($rectorRecipe->getExtraFileContent()) {
$filePaths[] = __DIR__ . '/../../templates/rules/__package__/tests/Rector/__Category__/__Name__/__Extra__Name__Test.php.inc';
} else {
$filePaths[] = __DIR__ . '/../../templates/rules/__package__/tests/Rector/__Category__/__Name__/__Name__Test.php.inc';

View File

@ -7,7 +7,7 @@ namespace Rector\RectorGenerator\Generator;
use Nette\Utils\Strings;
use Rector\RectorGenerator\FileSystem\TemplateFileSystem;
use Rector\RectorGenerator\TemplateFactory;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Symplify\SmartFileSystem\SmartFileInfo;
use Symplify\SmartFileSystem\SmartFileSystem;
@ -45,7 +45,7 @@ final class FileGenerator
public function generateFiles(
array $templateFileInfos,
array $templateVariables,
RectorRecipeConfiguration $rectorRecipeConfiguration,
RectorRecipe $rectorRecipe,
string $destinationDirectory
): array {
$generatedFilePaths = [];
@ -54,7 +54,7 @@ final class FileGenerator
$generatedFilePaths[] = $this->generateFileInfoWithTemplateVariables(
$fileInfo,
$templateVariables,
$rectorRecipeConfiguration,
$rectorRecipe,
$destinationDirectory
);
}
@ -65,20 +65,20 @@ final class FileGenerator
private function generateFileInfoWithTemplateVariables(
SmartFileInfo $smartFileInfo,
array $templateVariables,
RectorRecipeConfiguration $rectorRecipeConfiguration,
RectorRecipe $rectorRecipe,
string $targetDirectory
): string {
$targetFilePath = $this->templateFileSystem->resolveDestination(
$smartFileInfo,
$templateVariables,
$rectorRecipeConfiguration,
$rectorRecipe,
$targetDirectory
);
$content = $this->templateFactory->create($smartFileInfo->getContents(), $templateVariables);
// replace "Rector\Utils\" with "Utils\Rector\" for 3rd party packages
if (! $rectorRecipeConfiguration->isRectorRepository()) {
if (! $rectorRecipe->isRectorRepository()) {
$content = Strings::replace($content, '#Rector\\\\Utils#', 'Utils\Rector');
}

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Rector\RectorGenerator\Guard;
use Rector\RectorGenerator\FileSystem\TemplateFileSystem;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symplify\SmartFileSystem\SmartFileInfo;
@ -33,13 +33,13 @@ final class OverrideGuard
public function isUnwantedOverride(
array $templateFileInfos,
array $templateVariables,
RectorRecipeConfiguration $rectorRecipeConfiguration,
RectorRecipe $rectorRecipe,
string $targetDirectory
): bool {
foreach ($templateFileInfos as $templateFileInfo) {
if (! $this->doesFileInfoAlreadyExist(
$templateVariables,
$rectorRecipeConfiguration,
$rectorRecipe,
$templateFileInfo,
$targetDirectory
)) {
@ -54,14 +54,14 @@ final class OverrideGuard
private function doesFileInfoAlreadyExist(
array $templateVariables,
RectorRecipeConfiguration $rectorRecipeConfiguration,
RectorRecipe $rectorRecipe,
SmartFileInfo $templateFileInfo,
string $targetDirectory
): bool {
$destination = $this->templateFileSystem->resolveDestination(
$templateFileInfo,
$templateVariables,
$rectorRecipeConfiguration,
$rectorRecipe,
$targetDirectory
);

View File

@ -1,78 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\RectorGenerator\Guard;
use PhpParser\Node\Expr\FuncCall;
use Rector\Core\Configuration\Option;
use Rector\RectorGenerator\Exception\ConfigurationException;
use Rector\RectorGenerator\ValueObject\RecipeOption;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
final class RecipeGuard
{
/**
* @var string[]
*/
private const REQUIRED_KEYS = [
RecipeOption::PACKAGE,
RecipeOption::NAME,
RecipeOption::NODE_TYPES,
RecipeOption::CODE_BEFORE,
RecipeOption::CODE_AFTER,
RecipeOption::DESCRIPTION,
RecipeOption::SOURCE,
RecipeOption::SET,
];
/**
* @param mixed[] $rectorRecipe
*/
public function ensureRecipeIsValid(array $rectorRecipe, bool $isRectorRepository): void
{
if ($rectorRecipe === []) {
$message = 'Make sure the "rector-recipe.php" config file is imported. Are you sure its in your main config?';
throw new ConfigurationException($message);
}
$requiredKeysCount = count(self::REQUIRED_KEYS);
$providedKeys = array_intersect(array_keys($rectorRecipe), self::REQUIRED_KEYS);
$providedKeysCount = count($providedKeys);
if ($providedKeysCount !== $requiredKeysCount) {
$message = sprintf(
'Make sure "%s" keys are present in "parameters > %s"',
Option::RECTOR_RECIPE,
implode('", "', self::REQUIRED_KEYS)
);
throw new ConfigurationException($message);
}
if (count($rectorRecipe[RecipeOption::NODE_TYPES]) < 1) {
$message = sprintf(
'"%s" option requires at least one node, e.g. "%s"',
FuncCall::class,
RecipeOption::NODE_TYPES
);
throw new ConfigurationException($message);
}
$this->validatePackageOption($rectorRecipe[RecipeOption::PACKAGE], $isRectorRepository);
}
private function validatePackageOption(?string $package, bool $isRectorRepository): void
{
if ($package !== '' && $package !== null && $package !== RectorRecipeConfiguration::PACKAGE_UTILS) {
return;
}
// only can be empty or utils when outside Rector repository
if (! $isRectorRepository) {
return;
}
$message = sprintf('Parameter "package" cannot be empty or "Utils", when in Rector repository');
throw new ConfigurationException($message);
}
}

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Rector\RectorGenerator\Provider;
use Rector\RectorGenerator\Exception\ConfigurationException;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
final class RectorRecipeProvider
{
/**
* @var string
*/
private const MESSAGE = 'Make sure the "rector-recipe.php" config file is imported and parameter set. Are you sure its in your main config?';
/**
* @var RectorRecipe|null
*/
private $rectorRecipe;
/**
* Parameter must be configured in the rector config
*/
public function __construct(?RectorRecipe $rectorRecipe = null)
{
$this->rectorRecipe = $rectorRecipe;
}
public function provide(): RectorRecipe
{
if ($this->rectorRecipe === null) {
throw new ConfigurationException(self::MESSAGE);
}
return $this->rectorRecipe;
}
}

View File

@ -14,7 +14,7 @@ use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
use Rector\RectorGenerator\Config\ConfigFilesystem;
use Rector\RectorGenerator\NodeFactory\ConfigurationNodeFactory;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
final class TemplateVariablesFactory
{
@ -68,56 +68,56 @@ final class TemplateVariablesFactory
/**
* @return string[]
*/
public function createFromConfiguration(RectorRecipeConfiguration $rectorRecipeConfiguration): array
public function createFromRectorRecipe(RectorRecipe $rectorRecipe): array
{
$data = [
self::VARIABLE_PACKAGE => $rectorRecipeConfiguration->getPackage(),
self::VARIABLE_PACKAGE_LOWERCASE => $rectorRecipeConfiguration->getPackageDirectory(),
'__Category__' => $rectorRecipeConfiguration->getCategory(),
'__Description__' => $rectorRecipeConfiguration->getDescription(),
'__Name__' => $rectorRecipeConfiguration->getName(),
'__CodeBefore__' => trim($rectorRecipeConfiguration->getCodeBefore()) . PHP_EOL,
'__CodeBeforeExample__' => $this->createCodeForDefinition($rectorRecipeConfiguration->getCodeBefore()),
'__CodeAfter__' => trim($rectorRecipeConfiguration->getCodeAfter()) . PHP_EOL,
'__CodeAfterExample__' => $this->createCodeForDefinition($rectorRecipeConfiguration->getCodeAfter()),
'__Source__' => $this->createSourceDocBlock($rectorRecipeConfiguration->getSource()),
self::VARIABLE_PACKAGE => $rectorRecipe->getPackage(),
self::VARIABLE_PACKAGE_LOWERCASE => $rectorRecipe->getPackageDirectory(),
'__Category__' => $rectorRecipe->getCategory(),
'__Description__' => $rectorRecipe->getDescription(),
'__Name__' => $rectorRecipe->getName(),
'__CodeBefore__' => trim($rectorRecipe->getCodeBefore()) . PHP_EOL,
'__CodeBeforeExample__' => $this->createCodeForDefinition($rectorRecipe->getCodeBefore()),
'__CodeAfter__' => trim($rectorRecipe->getCodeAfter()) . PHP_EOL,
'__CodeAfterExample__' => $this->createCodeForDefinition($rectorRecipe->getCodeAfter()),
'__Resources__' => $this->createSourceDocBlock($rectorRecipe->getResources()),
];
$rectorClass = $this->templateFactory->create(ConfigFilesystem::RECTOR_FQN_NAME_PATTERN, $data);
if ($rectorRecipeConfiguration->getRuleConfiguration() !== []) {
if ($rectorRecipe->getConfigration() !== []) {
$data['__TestRuleConfiguration__'] = $this->createRuleConfiguration(
$rectorClass,
$rectorRecipeConfiguration->getRuleConfiguration()
$rectorRecipe->getConfigration()
);
$data['__RuleConfiguration__'] = $this->createRuleConfiguration(
self::SELF,
$rectorRecipeConfiguration->getRuleConfiguration()
$rectorRecipe->getConfigration()
);
$data['__ConfigurationProperties__'] = $this->createConfigurationProperty(
$rectorRecipeConfiguration->getRuleConfiguration()
$rectorRecipe->getConfigration()
);
$data['__ConfigurationConstants__'] = $this->createConfigurationConstants(
$rectorRecipeConfiguration->getRuleConfiguration()
$rectorRecipe->getConfigration()
);
$data['__ConfigureClassMethod__'] = $this->createConfigureClassMethod(
$rectorRecipeConfiguration->getRuleConfiguration()
$rectorRecipe->getConfigration()
);
}
if ($rectorRecipeConfiguration->getExtraFileContent() !== null && $rectorRecipeConfiguration->getExtraFileName() !== null) {
$data['__ExtraFileName__'] = $rectorRecipeConfiguration->getExtraFileName();
$data['__ExtraFileContent__'] = trim($rectorRecipeConfiguration->getExtraFileContent()) . PHP_EOL;
if ($rectorRecipe->getExtraFileContent() !== null && $rectorRecipe->getExtraFileName() !== null) {
$data['__ExtraFileName__'] = $rectorRecipe->getExtraFileName();
$data['__ExtraFileContent__'] = trim($rectorRecipe->getExtraFileContent()) . PHP_EOL;
$data['__ExtraFileContentExample__'] = $this->createCodeForDefinition(
$rectorRecipeConfiguration->getExtraFileContent()
$rectorRecipe->getExtraFileContent()
);
}
$data['__NodeTypesPhp__'] = $this->createNodeTypePhp($rectorRecipeConfiguration);
$data['__NodeTypesDoc__'] = '\\' . implode('|\\', $rectorRecipeConfiguration->getNodeTypes());
$data['__NodeTypesPhp__'] = $this->createNodeTypePhp($rectorRecipe);
$data['__NodeTypesDoc__'] = '\\' . implode('|\\', $rectorRecipe->getNodeTypes());
return $data;
}
@ -199,10 +199,10 @@ final class TemplateVariablesFactory
return $this->betterStandardPrinter->print($classMethod);
}
private function createNodeTypePhp(RectorRecipeConfiguration $rectorRecipeConfiguration): string
private function createNodeTypePhp(RectorRecipe $rectorRecipe): string
{
$referencingClassConsts = [];
foreach ($rectorRecipeConfiguration->getNodeTypes() as $nodeType) {
foreach ($rectorRecipe->getNodeTypes() as $nodeType) {
$referencingClassConsts[] = $this->nodeFactory->createClassConstReference($nodeType);
}

View File

@ -1,63 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\RectorGenerator\ValueObject;
final class RecipeOption
{
/**
* @var string
*/
public const PACKAGE = 'package';
/**
* @var string
*/
public const NAME = 'name';
/**
* @var string
*/
public const NODE_TYPES = 'node_types';
/**
* @var string
*/
public const DESCRIPTION = 'description';
/**
* @var string
*/
public const CODE_BEFORE = 'code_before';
/**
* @var string
*/
public const CODE_AFTER = 'code_after';
/**
* @var string
*/
public const RULE_CONFIGURATION = 'rule_configuration';
/**
* @var string
*/
public const SOURCE = 'source';
/**
* @var string
*/
public const SET = 'set';
/**
* @var string
*/
public const EXTRA_FILE_CONTENT = 'extra_file_content';
/**
* @var string
*/
public const EXTRA_FILE_NAME = 'extra_file_name';
}

View File

@ -0,0 +1,326 @@
<?php
declare(strict_types=1);
namespace Rector\RectorGenerator\ValueObject;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Util\StaticRectorStrings;
use Rector\RectorGenerator\Exception\ConfigurationException;
final class RectorRecipe
{
/**
* @var string
*/
private const PACKAGE_UTILS = 'Utils';
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $description;
/**
* @var string
*/
private $codeBefore;
/**
* @var string
*/
private $codeAfter;
/**
* @var bool
*/
private $isRectorRepository = false;
/**
* @var bool
*/
private $isPhpSnippet = true;
/**
* @var string
*/
private $category;
/**
* @var class-string[]
*/
private $nodeTypes = [];
/**
* @var mixed[]
*/
private $resources = [];
/**
* @var mixed[]
*/
private $configration = [];
/**
* @var string|null
*/
private $package;
/**
* @var string|null
*/
private $set;
/**
* @var string|null
*/
private $extraFileName;
/**
* @var string|null
*/
private $extraFileContent;
/**
* @param class-string[] $nodeTypes
* @param mixed[] $configration
*/
public function __construct(
?string $package,
string $name,
array $nodeTypes,
string $description,
string $codeBefore,
string $codeAfter,
array $resources,
?string $set = null,
array $configration = [],
?string $extraFileName = null,
?string $extraFileContent = null,
?bool $isRectorRepository = null
) {
$this->setName($name);
$this->setNodeTypes($nodeTypes);
$this->description = $description;
$this->setCodeBefore($codeBefore);
$this->setCodeAfter($codeAfter);
$this->setResources($resources);
$this->set = $set;
$this->configration = $configration;
$this->extraFileName = $extraFileName;
$this->setExtraFileContent($extraFileContent);
$this->setIsRectorRepository($isRectorRepository);
// last on purpose, depends on isRectorRepository
$this->setPackage($package);
$this->resolveCategory();
}
public function getPackage(): string
{
$recipePackage = $this->package;
if (! $this->isRectorRepository || $recipePackage === null || $recipePackage === '') {
return self::PACKAGE_UTILS;
}
return $recipePackage;
}
public function getName(): string
{
return $this->name;
}
public function getNodeTypes(): array
{
return $this->nodeTypes;
}
public function getDescription(): string
{
return $this->description;
}
public function getCodeBefore(): string
{
return $this->codeBefore;
}
public function getCodeAfter(): string
{
return $this->codeAfter;
}
public function getResources(): array
{
return $this->resources;
}
public function getSet(): ?string
{
return $this->set;
}
/**
* @return array<string, mixed>
*/
public function getConfigration(): array
{
return $this->configration;
}
public function getExtraFileName(): ?string
{
return $this->extraFileName;
}
public function getExtraFileContent(): ?string
{
return $this->extraFileContent;
}
public function isPhpSnippet(): bool
{
return $this->isPhpSnippet;
}
public function isRectorRepository(): bool
{
return $this->isRectorRepository;
}
public function getCategory(): string
{
return $this->category;
}
public function getPackageDirectory(): string
{
if (! $this->isRectorRepository) {
return 'rector';
}
// special cases
if ($this->getPackage() === 'PHPUnit') {
return 'phpunit';
}
return StaticRectorStrings::camelCaseToDashes($this->getPackage());
}
private function setName(string $name): void
{
if (! Strings::endsWith($name, 'Rector')) {
$message = sprintf('Rector name "%s" must end with "Rector"', $name);
throw new ConfigurationException($message);
}
$this->name = $name;
}
/**
* @param class-string[] $nodeTypes
*/
private function setNodeTypes(array $nodeTypes): void
{
foreach ($nodeTypes as $nodeType) {
if (is_a($nodeType, Node::class, true)) {
continue;
}
$message = sprintf(
'Node type "%s" does not exist, implement "%s" interface or is not imported in "rector-recipe.php"',
$nodeType,
Node::class
);
throw new ShouldNotHappenException($message);
}
if (count($nodeTypes) < 1) {
$message = sprintf('"$nodeTypes" argument requires at least one item, e.g. "%s"', FuncCall::class);
throw new ConfigurationException($message);
}
$this->nodeTypes = $nodeTypes;
}
private function setCodeBefore(string $codeBefore): string
{
$this->setIsPhpSnippet($codeBefore);
$codeBefore = $this->normalizeCode($codeBefore);
return $this->codeBefore = $codeBefore;
}
private function setCodeAfter(string $codeAfter): string
{
$codeAfter = $this->normalizeCode($codeAfter);
return $this->codeAfter = $codeAfter;
}
private function setResources(array $resources): void
{
$this->resources = array_filter($resources);
}
private function setExtraFileContent(?string $extraFileContent): void
{
if ($extraFileContent === null) {
return;
}
$this->extraFileContent = $this->normalizeCode($extraFileContent);
}
private function setIsRectorRepository(?bool $isRectorRepository): void
{
$this->isRectorRepository = $isRectorRepository ?? file_exists(__DIR__ . '/../../../../vendor');
}
private function setPackage(?string $package): void
{
if ($package !== '' && $package !== null && $package !== self::PACKAGE_UTILS) {
$this->package = $package;
return;
}
// only can be empty or utils when outside Rector repository
if (! $this->isRectorRepository) {
$this->package = $package;
return;
}
$message = sprintf('Parameter "package" cannot be empty or "Utils", when in Rector repository');
throw new ConfigurationException($message);
}
private function resolveCategory(): void
{
$this->category = (string) Strings::after($this->nodeTypes[0], '\\', -1);
}
private function setIsPhpSnippet(string $codeBefore): void
{
$this->isPhpSnippet = Strings::startsWith($codeBefore, '<?php');
}
private function normalizeCode(string $code): string
{
if (Strings::startsWith($code, '<?php')) {
$code = ltrim($code, '<?php');
}
return trim($code);
}
}

View File

@ -1,256 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\RectorGenerator\ValueObject;
use Nette\Utils\Strings;
use PhpParser\Node;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Util\StaticRectorStrings;
use Symplify\SetConfigResolver\ValueObject\Set;
use Symplify\SmartFileSystem\SmartFileInfo;
final class RectorRecipeConfiguration
{
/**
* @var string
*/
public const PACKAGE_UTILS = 'Utils';
/**
* @var bool
*/
private $isRectorRepository = false;
/**
* @var string
*/
private $name;
/**
* @var string
*/
private $category;
/**
* @var string
*/
private $codeBefore;
/**
* @var string
*/
private $codeAfter;
/**
* @var string
*/
private $description;
/**
* @var bool
*/
private $isPhpSnippet = false;
/**
* @var string[]
*/
private $nodeTypes = [];
/**
* @var string[]
*/
private $source = [];
/**
* @var array<string, mixed>
*/
private $ruleConfiguration = [];
/**
* @var string|null
*/
private $package;
/**
* @var Set|null
*/
private $set;
/**
* @var string|null
*/
private $extraFileContent;
/**
* @var string|null
*/
private $extraFileName;
/**
* @param string[] $nodeTypes
* @param string[] $source
*/
public function __construct(
?string $package,
string $name,
string $category,
array $nodeTypes,
string $description,
string $codeBefore,
string $codeAfter,
?string $extraFileContent,
?string $extraFileName,
array $ruleConfiguration,
array $source,
?Set $set,
bool $isPhpSnippet,
bool $isRectorRepository
) {
$this->package = $package;
$this->setName($name);
$this->category = $category;
$this->setNodeTypes($nodeTypes);
$this->codeBefore = $codeBefore;
$this->codeAfter = $codeAfter;
$this->description = $description;
$this->source = $source;
$this->set = $set;
$this->isPhpSnippet = $isPhpSnippet;
$this->extraFileContent = $extraFileContent;
$this->extraFileName = $extraFileName;
$this->ruleConfiguration = $ruleConfiguration;
$this->isRectorRepository = $isRectorRepository;
}
public function getDescription(): string
{
return $this->description;
}
public function getPackage(): string
{
if (! $this->isRectorRepository || $this->package === null || $this->package === '') {
return self::PACKAGE_UTILS;
}
return $this->package;
}
public function getPackageDirectory(): string
{
if (! $this->isRectorRepository) {
return 'rector';
}
// special cases
if ($this->package === 'PHPUnit') {
return 'phpunit';
}
return StaticRectorStrings::camelCaseToDashes($this->getPackage());
}
public function getName(): string
{
return $this->name;
}
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return $this->nodeTypes;
}
public function getCategory(): string
{
return $this->category;
}
public function getCodeBefore(): string
{
return $this->codeBefore;
}
public function getCodeAfter(): string
{
return $this->codeAfter;
}
/**
* @return string[]
*/
public function getSource(): array
{
return $this->source;
}
public function getSetConfig(): ?SmartFileInfo
{
if ($this->set === null) {
return null;
}
return $this->set->getSetFileInfo();
}
public function isPhpSnippet(): bool
{
return $this->isPhpSnippet;
}
public function getExtraFileContent(): ?string
{
return $this->extraFileContent;
}
public function getExtraFileName(): ?string
{
return $this->extraFileName;
}
/**
* @return array<string, mixed>
*/
public function getRuleConfiguration(): array
{
return $this->ruleConfiguration;
}
public function isRectorRepository(): bool
{
return $this->isRectorRepository;
}
private function setName(string $name): void
{
if (! Strings::endsWith($name, 'Rector')) {
$message = sprintf('Rector name "%s" must end with "Rector"', $name);
throw new ShouldNotHappenException($message);
}
$this->name = $name;
}
/**
* @param string[] $nodeTypes
*/
private function setNodeTypes(array $nodeTypes): void
{
foreach ($nodeTypes as $nodeType) {
if (! is_a($nodeType, Node::class, true)) {
$message = sprintf(
'Node type "%s" does not exist, implement "%s" interface, or not imported in Rector recipe',
$nodeType,
Node::class
);
throw new ShouldNotHappenException($message);
}
}
$this->nodeTypes = $nodeTypes;
}
}

View File

@ -11,7 +11,7 @@ use Rector\Core\RectorDefinition\ConfiguredCodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
/**
__Source__
__Resources__
* @see \Rector\__Package__\Tests\Rector\__Category__\__Name__\__Name__Test
*/
final class __Name__ extends AbstractRector implements ConfigurableRectorInterface

View File

@ -10,7 +10,7 @@ use Rector\Core\RectorDefinition\CodeSample;
use Rector\Core\RectorDefinition\RectorDefinition;
/**
__Source__
__Resources__
* @see \Rector\__Package__\Tests\Rector\__Category__\__Name__\__Name__Test
*/
final class __Name__ extends AbstractRector

View File

@ -5,12 +5,11 @@ declare(strict_types=1);
namespace Rector\RectorGenerator\Tests\RectorGenerator;
use Rector\Core\HttpKernel\RectorKernel;
use Rector\RectorGenerator\Configuration\ConfigurationFactory;
use Rector\RectorGenerator\Finder\TemplateFinder;
use Rector\RectorGenerator\Generator\FileGenerator;
use Rector\RectorGenerator\TemplateVariablesFactory;
use Rector\RectorGenerator\Tests\RectorGenerator\Source\StaticRectorRecipeFactory;
use Rector\RectorGenerator\ValueObject\RectorRecipeConfiguration;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Symplify\EasyTesting\PHPUnit\Behavior\DirectoryAssertableTrait;
use Symplify\PackageBuilder\Tests\AbstractKernelTestCase;
use Symplify\SmartFileSystem\SmartFileSystem;
@ -24,11 +23,6 @@ final class RectorGeneratorTest extends AbstractKernelTestCase
*/
private const DESTINATION_DIRECTORY = __DIR__ . '/__temp';
/**
* @var ConfigurationFactory
*/
private $configurationFactory;
/**
* @var TemplateVariablesFactory
*/
@ -53,7 +47,6 @@ final class RectorGeneratorTest extends AbstractKernelTestCase
{
$this->bootKernel(RectorKernel::class);
$this->configurationFactory = self::$container->get(ConfigurationFactory::class);
$this->templateVariablesFactory = self::$container->get(TemplateVariablesFactory::class);
$this->templateFinder = self::$container->get(TemplateFinder::class);
$this->fileGenerator = self::$container->get(FileGenerator::class);
@ -83,21 +76,20 @@ final class RectorGeneratorTest extends AbstractKernelTestCase
private function doGenerateFiles(bool $isRectorRepository): void
{
$rectorRecipeConfiguration = $this->createConfiguration($isRectorRepository);
$templateFileInfos = $this->templateFinder->find($rectorRecipeConfiguration);
$templateVariables = $this->templateVariablesFactory->createFromConfiguration($rectorRecipeConfiguration);
$rectorRecipe = $this->createConfiguration($isRectorRepository);
$templateFileInfos = $this->templateFinder->find($rectorRecipe);
$templateVariables = $this->templateVariablesFactory->createFromRectorRecipe($rectorRecipe);
$this->fileGenerator->generateFiles(
$templateFileInfos,
$templateVariables,
$rectorRecipeConfiguration,
$rectorRecipe,
self::DESTINATION_DIRECTORY
);
}
private function createConfiguration(bool $isRectorRepository): RectorRecipeConfiguration
private function createConfiguration(bool $isRectorRepository): RectorRecipe
{
$rectorRecipe = StaticRectorRecipeFactory::createWithConfiguration();
return $this->configurationFactory->createFromRectorRecipe($rectorRecipe, $isRectorRepository);
return StaticRectorRecipeFactory::createRectorRecipe($isRectorRepository);
}
}

View File

@ -5,20 +5,18 @@ declare(strict_types=1);
namespace Rector\RectorGenerator\Tests\RectorGenerator\Source;
use PhpParser\Node\Expr\MethodCall;
use Rector\RectorGenerator\ValueObject\RecipeOption;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
final class StaticRectorRecipeFactory
{
public static function createWithConfiguration(): array
public static function createRectorRecipe(bool $isRectorRepository): RectorRecipe
{
return [
RecipeOption::PACKAGE => 'ModeratePackage',
RecipeOption::NAME => 'WhateverRector',
RecipeOption::NODE_TYPES => [
MethodCall::class,
],
RecipeOption::DESCRIPTION => 'Change $service->arg(...) to $service->call(...)',
RecipeOption::CODE_BEFORE => <<<'CODE_SAMPLE'
return new RectorRecipe(
'ModeratePackage',
'WhateverRector',
[MethodCall::class],
'Change $service->arg(...) to $service->call(...)',
<<<'CODE_SAMPLE'
<?php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
@ -30,7 +28,7 @@ return static function (ContainerConfigurator $containerConfigurator): void {
->arg('$key', 'value');
}
CODE_SAMPLE,
RecipeOption::CODE_AFTER => <<<'CODE_SAMPLE'
<<<'CODE_SAMPLE'
<?php
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
@ -45,20 +43,19 @@ return static function (ContainerConfigurator $containerConfigurator): void {
}
CODE_SAMPLE,
// e.g. link to RFC or headline in upgrade guide, 1 or more in the list
RecipeOption::SOURCE => null,
[],
// e.g. symfony30, target set to add this Rule to; keep null if part of core
RecipeOption::SET => null,
null,
// OPTIONAL: only when configured
RecipeOption::RULE_CONFIGURATION => [
[
'CLASS_TYPE_TO_METHOD_NAME' => [
'SomeClass' => 'configure'
]
],
// OPTIONAL: extra file
RecipeOption::EXTRA_FILE_NAME => null,
RecipeOption::EXTRA_FILE_CONTENT => null,
];
null,
null,
$isRectorRepository
);
}
}

View File

@ -2,64 +2,68 @@
declare(strict_types=1);
use PhpParser\Node\Expr\MethodCall;
use Rector\Core\Configuration\Option;
use Rector\RectorGenerator\ValueObject\RecipeOption;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\RectorGenerator\Provider\RectorRecipeProvider;
use Rector\RectorGenerator\ValueObject\RectorRecipe;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Rector\SymfonyPhpConfig\inline_object;
// run "bin/rector generate" to create a new Rector + tests from this config
// run "bin/rector generate" to a new Rector basic schema + tests from this config
return static function (ContainerConfigurator $containerConfigurator): void {
$parameters = $containerConfigurator->parameters();
$services = $containerConfigurator->services();
$parameters->set(Option::RECTOR_RECIPE, [
// what category the package belongs to
RecipeOption::PACKAGE => 'Nette',
$rectorRecipe = new RectorRecipe(
// category
'Symfony',
// name
'RenameClassMethod',
// node types - this will get in "getNodeTypes()" method
[ClassMethod::class],
// description
'Change some class method to different name',
// name the rule, what it does, in a something to something else
RecipeOption::NAME => 'TranslateClassMethodToVariadicsRector',
// basically content of "getNodeTypes()"
RecipeOption::NODE_TYPES => [
MethodCall::class,
],
RecipeOption::DESCRIPTION => 'Rename $name variable to $fullname',
RecipeOption::CODE_BEFORE => <<<'CODE_SAMPLE'
// code before
<<<'CODE_SAMPLE'
<?php
final class SomeClass
class SomeClass
{
public function run()
{
return $name;
}
}
CODE_SAMPLE,
RecipeOption::CODE_AFTER => <<<'CODE_SAMPLE'
// code after
<<<'CODE_SAMPLE'
<?php
final class SomeClass
class SomeClass
{
public function run()
public function go()
{
return $fullname;
}
}
CODE_SAMPLE,
// e.g. link to RFC or headline in upgrade guide, 1 or more in the list
RecipeOption::SOURCE => [
'https://github.com/nette/utils/pull/178'
// informational resources on "why" the change, e.g. RFC, upgrade guide on Github
[
'https://...'
],
// OPTIONAL ↓
// e.g. symfony30, target set to add this Rule to
RecipeOption::SET => null,
// e.g. symfony30, target set to add this Rule to; keep null if part of core
// set, use `SetList::SOME_CONSTANT` for this
null,
// only when configured
RecipeOption::RULE_CONFIGURATION => null,
// configuration in array
[],
// VERY OPTIONAL ↓ only when configured
RecipeOption::EXTRA_FILE_NAME => null,
RecipeOption::EXTRA_FILE_CONTENT => null,
]);
// extra file name
null,
// extra file content
null
);
$services->set(RectorRecipeProvider::class)
->arg('$rectorRecipe', inline_object($rectorRecipe));
};

View File

@ -9,6 +9,16 @@ use ReflectionClass;
use function Symfony\Component\DependencyInjection\Loader\Configurator\inline_service;
use Symfony\Component\DependencyInjection\Loader\Configurator\InlineServiceConfigurator;
function inline_object(object $object): InlineServiceConfigurator
{
$reflectionClass = new ReflectionClass($object);
$className = $reflectionClass->getName();
$argumentValues = resolve_argument_values($reflectionClass, $object);
return inline_service($className)->args($argumentValues);
}
/**
* @param object[] $objects
* @return InlineServiceConfigurator[]
@ -17,12 +27,7 @@ function inline_objects(array $objects): array
{
$inlineServices = [];
foreach ($objects as $object) {
$reflectionClass = new ReflectionClass($object);
$className = $reflectionClass->getName();
$argumentValues = resolve_argument_values($reflectionClass, $object);
$inlineServices[] = inline_service($className)->args($argumentValues);
$inlineServices[] = inline_object($object);
}
return $inlineServices;

View File

@ -29,9 +29,4 @@ final class SetResolver
return $this->setProvider->provideByName($setOption);
}
public function resolveSetByName(string $name): ?Set
{
return $this->setProvider->provideByName($name);
}
}

View File

@ -91,9 +91,17 @@ abstract class AbstractGenericRectorTestCase extends AbstractKernelTestCase
if (defined('RECTOR_REPOSITORY')) {
$this->createRectorRepositoryContainer();
} else {
$rootRectorPhp = getcwd() . '/rector.php';
$configs = [];
if (file_exists($rootRectorPhp)) {
$configs[] = $rootRectorPhp;
}
// 3rd party
$configFor3rdPartyTest = $this->getConfigFor3rdPartyTest();
$this->bootKernelWithConfigs(RectorKernel::class, [$configFor3rdPartyTest]);
$configs[] = $this->getConfigFor3rdPartyTest();
$this->bootKernelWithConfigs(RectorKernel::class, $configs);
}
$enabledRectorsProvider = self::$container->get(EnabledRectorsProvider::class);