mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-08 04:10:51 +00:00
[RectorGenerator] Refactoring to testable code
This commit is contained in:
parent
f4c741f3f9
commit
2741893bbf
|
@ -171,6 +171,7 @@
|
|||
"Rector\\JMS\\Tests\\": "rules/jms/tests",
|
||||
"Rector\\Laravel\\Tests\\": "rules/laravel/tests",
|
||||
"Rector\\Legacy\\Tests\\": "rules/legacy/tests",
|
||||
"Rector\\RectorGenerator\\Tests\\": "packages/rector-generator/tests",
|
||||
"Rector\\MagicDisclosure\\Tests\\": "rules/magic-disclosure/tests",
|
||||
"Rector\\MysqlToMysqli\\Tests\\": "rules/mysql-to-mysqli/tests",
|
||||
"Rector\\NetteTesterToPHPUnit\\Tests\\": "rules/nette-tester-to-phpunit/tests",
|
||||
|
@ -262,7 +263,6 @@
|
|||
"scripts": {
|
||||
"complete-check": [
|
||||
"@check-cs",
|
||||
"bin/rector validate-fixtures --ansi",
|
||||
"bin/rector validate-services-in-sets --ansi",
|
||||
"bin/rector validate-sets --ansi",
|
||||
"phpunit",
|
||||
|
|
|
@ -6,15 +6,14 @@ 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\FileSystem\TemplateFileSystem;
|
||||
use Rector\RectorGenerator\Finder\TemplateFinder;
|
||||
use Rector\RectorGenerator\Generator\FileGenerator;
|
||||
use Rector\RectorGenerator\Guard\OverrideGuard;
|
||||
use Rector\RectorGenerator\TemplateFactory;
|
||||
use Rector\RectorGenerator\TemplateVariablesFactory;
|
||||
use Rector\RectorGenerator\ValueObject\Configuration;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
@ -23,20 +22,9 @@ use Symplify\PackageBuilder\Console\Command\CommandNaming;
|
|||
use Symplify\PackageBuilder\Console\ShellCode;
|
||||
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
final class CreateCommand extends Command
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $testCasePath;
|
||||
|
||||
/**
|
||||
* @var string[]
|
||||
*/
|
||||
private $generatedFiles = [];
|
||||
|
||||
/**
|
||||
* @var SymfonyStyle
|
||||
*/
|
||||
|
@ -62,16 +50,6 @@ final class CreateCommand extends Command
|
|||
*/
|
||||
private $templateFinder;
|
||||
|
||||
/**
|
||||
* @var TemplateFileSystem
|
||||
*/
|
||||
private $templateFileSystem;
|
||||
|
||||
/**
|
||||
* @var TemplateFactory
|
||||
*/
|
||||
private $templateFactory;
|
||||
|
||||
/**
|
||||
* @var ConfigFilesystem
|
||||
*/
|
||||
|
@ -82,26 +60,24 @@ final class CreateCommand extends Command
|
|||
*/
|
||||
private $overrideGuard;
|
||||
|
||||
/**
|
||||
* @var SmartFileSystem
|
||||
*/
|
||||
private $smartFileSystem;
|
||||
|
||||
/**
|
||||
* @var ParameterProvider
|
||||
*/
|
||||
private $parameterProvider;
|
||||
|
||||
/**
|
||||
* @var FileGenerator
|
||||
*/
|
||||
private $fileGenerator;
|
||||
|
||||
public function __construct(
|
||||
ComposerPackageAutoloadUpdater $composerPackageAutoloadUpdater,
|
||||
ConfigFilesystem $configFilesystem,
|
||||
ConfigurationFactory $configurationFactory,
|
||||
FileGenerator $fileGenerator,
|
||||
OverrideGuard $overrideGuard,
|
||||
ParameterProvider $parameterProvider,
|
||||
SmartFileSystem $smartFileSystem,
|
||||
SymfonyStyle $symfonyStyle,
|
||||
TemplateFactory $templateFactory,
|
||||
TemplateFileSystem $templateFileSystem,
|
||||
TemplateFinder $templateFinder,
|
||||
TemplateVariablesFactory $templateVariablesFactory
|
||||
) {
|
||||
|
@ -112,12 +88,10 @@ final class CreateCommand extends Command
|
|||
$this->templateVariablesFactory = $templateVariablesFactory;
|
||||
$this->composerPackageAutoloadUpdater = $composerPackageAutoloadUpdater;
|
||||
$this->templateFinder = $templateFinder;
|
||||
$this->templateFileSystem = $templateFileSystem;
|
||||
$this->templateFactory = $templateFactory;
|
||||
$this->configFilesystem = $configFilesystem;
|
||||
$this->overrideGuard = $overrideGuard;
|
||||
$this->smartFileSystem = $smartFileSystem;
|
||||
$this->parameterProvider = $parameterProvider;
|
||||
$this->fileGenerator = $fileGenerator;
|
||||
}
|
||||
|
||||
protected function configure(): void
|
||||
|
@ -139,10 +113,13 @@ final class CreateCommand extends Command
|
|||
|
||||
$templateFileInfos = $this->templateFinder->find($configuration);
|
||||
|
||||
$targetDirectory = getcwd();
|
||||
|
||||
$isUnwantedOverride = $this->overrideGuard->isUnwantedOverride(
|
||||
$templateFileInfos,
|
||||
$templateVariables,
|
||||
$configuration
|
||||
$configuration->getPackage(),
|
||||
$targetDirectory
|
||||
);
|
||||
if ($isUnwantedOverride) {
|
||||
$this->symfonyStyle->warning('No files were changed');
|
||||
|
@ -150,55 +127,52 @@ final class CreateCommand extends Command
|
|||
return ShellCode::SUCCESS;
|
||||
}
|
||||
|
||||
$this->generateFiles($templateFileInfos, $templateVariables, $configuration);
|
||||
$generatedFilePaths = $this->fileGenerator->generateFiles(
|
||||
$templateFileInfos,
|
||||
$templateVariables,
|
||||
$configuration,
|
||||
$targetDirectory
|
||||
);
|
||||
|
||||
$testCaseFilePath = $this->resolveTestCaseFilePath($generatedFilePaths);
|
||||
|
||||
$this->configFilesystem->appendRectorServiceToSet($configuration, $templateVariables);
|
||||
|
||||
$this->printSuccess($configuration->getName());
|
||||
$this->printSuccess($configuration->getName(), $generatedFilePaths, $testCaseFilePath);
|
||||
|
||||
return ShellCode::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SmartFileInfo[] $templateFileInfos
|
||||
* @param string[] $generatedFilePaths
|
||||
*/
|
||||
private function generateFiles(
|
||||
array $templateFileInfos,
|
||||
array $templateVariables,
|
||||
Configuration $configuration
|
||||
): void {
|
||||
foreach ($templateFileInfos as $smartFileInfo) {
|
||||
$destination = $this->templateFileSystem->resolveDestination(
|
||||
$smartFileInfo,
|
||||
$templateVariables,
|
||||
$configuration
|
||||
);
|
||||
|
||||
$content = $this->templateFactory->create($smartFileInfo->getContents(), $templateVariables);
|
||||
|
||||
$this->smartFileSystem->dumpFile($destination, $content);
|
||||
|
||||
$this->generatedFiles[] = $destination;
|
||||
|
||||
// is a test case?
|
||||
if (Strings::endsWith($destination, 'Test.php')) {
|
||||
$this->testCasePath = dirname($destination);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function printSuccess(string $name): void
|
||||
private function printSuccess(string $name, array $generatedFilePaths, string $testCaseFilePath): void
|
||||
{
|
||||
$message = sprintf('New files generated for "%s"', $name);
|
||||
$message = sprintf('New files generated for "%s":', $name);
|
||||
$this->symfonyStyle->title($message);
|
||||
sort($this->generatedFiles);
|
||||
$this->symfonyStyle->listing($this->generatedFiles);
|
||||
$message = sprintf(
|
||||
'Make tests green again:%svendor/bin/phpunit %s',
|
||||
PHP_EOL . PHP_EOL,
|
||||
$this->testCasePath
|
||||
);
|
||||
|
||||
sort($generatedFilePaths);
|
||||
$this->symfonyStyle->listing($generatedFilePaths);
|
||||
|
||||
$message = sprintf('Make tests green again:%svendor/bin/phpunit %s', PHP_EOL . PHP_EOL, $testCaseFilePath);
|
||||
|
||||
$this->symfonyStyle->success($message);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $generatedFilePaths
|
||||
*/
|
||||
private function resolveTestCaseFilePath(array $generatedFilePaths): string
|
||||
{
|
||||
foreach ($generatedFilePaths as $generatedFilePath) {
|
||||
if (! Strings::endsWith($generatedFilePath, 'Test.php')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$generatedFileInfo = new SmartFileInfo($generatedFilePath);
|
||||
return $generatedFileInfo->getRelativeFilePathFromCwd();
|
||||
}
|
||||
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,11 +35,6 @@ final class ComposerPackageAutoloadUpdater
|
|||
|
||||
public function processComposerAutoload(Configuration $configuration): void
|
||||
{
|
||||
// skip core, already autoloaded
|
||||
if ($configuration->getPackage() === 'Rector') {
|
||||
return;
|
||||
}
|
||||
|
||||
$composerJsonFilePath = getcwd() . '/composer.json';
|
||||
$composerJson = $this->jsonFileSystem->loadFileToJson($composerJsonFilePath);
|
||||
|
||||
|
@ -51,10 +46,10 @@ final class ComposerPackageAutoloadUpdater
|
|||
|
||||
// ask user
|
||||
$questionText = sprintf(
|
||||
'Can we update "composer.json" autoload with "%s" namespace?%s Handle it manually o therwise',
|
||||
'Should we update "composer.json" autoload with "%s" namespace?',
|
||||
$package->getSrcNamespace(),
|
||||
PHP_EOL
|
||||
);
|
||||
|
||||
$isConfirmed = $this->symfonyStyle->confirm($questionText);
|
||||
if (! $isConfirmed) {
|
||||
return;
|
||||
|
|
|
@ -20,7 +20,7 @@ final class ConfigFilesystem
|
|||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const RECTOR_FQN_NAME_PATTERN = 'Rector\__Package__\Rector\__Category__\__Name__';
|
||||
public const RECTOR_FQN_NAME_PATTERN = 'Rector\__Package__\Rector\__Category__\__Name__';
|
||||
|
||||
/**
|
||||
* @var TemplateFactory
|
||||
|
|
|
@ -6,7 +6,6 @@ namespace Rector\RectorGenerator\FileSystem;
|
|||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\RectorGenerator\Finder\TemplateFinder;
|
||||
use Rector\RectorGenerator\ValueObject\Configuration;
|
||||
use Rector\RectorGenerator\ValueObject\Package;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
|
@ -18,19 +17,22 @@ final class TemplateFileSystem
|
|||
public function resolveDestination(
|
||||
SmartFileInfo $smartFileInfo,
|
||||
array $templateVariables,
|
||||
Configuration $configuration
|
||||
string $package,
|
||||
string $targetDirectory
|
||||
): string {
|
||||
$destination = $smartFileInfo->getRelativeFilePathFromDirectory(TemplateFinder::TEMPLATES_DIRECTORY);
|
||||
|
||||
// normalize core package
|
||||
if ($configuration->getPackage() === Package::UTILS) {
|
||||
if ($package === Package::UTILS) {
|
||||
// special keyword for 3rd party Rectors, not for core Github contribution
|
||||
$destination = Strings::replace($destination, '#packages\/__Package__#', 'utils/rector');
|
||||
}
|
||||
|
||||
// remove _Configured|_Extra prefix
|
||||
$destination = $this->applyVariables($destination, $templateVariables);
|
||||
return Strings::replace($destination, '#(__Configured|__Extra)#', '');
|
||||
|
||||
$destination = Strings::replace($destination, '#(__Configured|__Extra)#', '');
|
||||
return $targetDirectory . DIRECTORY_SEPARATOR . $destination;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
82
packages/rector-generator/src/Generator/FileGenerator.php
Normal file
82
packages/rector-generator/src/Generator/FileGenerator.php
Normal file
|
@ -0,0 +1,82 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\RectorGenerator\Generator;
|
||||
|
||||
use Rector\RectorGenerator\FileSystem\TemplateFileSystem;
|
||||
use Rector\RectorGenerator\TemplateFactory;
|
||||
use Rector\RectorGenerator\ValueObject\Configuration;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
final class FileGenerator
|
||||
{
|
||||
/**
|
||||
* @var TemplateFileSystem
|
||||
*/
|
||||
private $templateFileSystem;
|
||||
|
||||
/**
|
||||
* @var TemplateFactory
|
||||
*/
|
||||
private $templateFactory;
|
||||
|
||||
/**
|
||||
* @var SmartFileSystem
|
||||
*/
|
||||
private $smartFileSystem;
|
||||
|
||||
public function __construct(
|
||||
SmartFileSystem $smartFileSystem,
|
||||
TemplateFactory $templateFactory,
|
||||
TemplateFileSystem $templateFileSystem
|
||||
) {
|
||||
$this->templateFileSystem = $templateFileSystem;
|
||||
$this->templateFactory = $templateFactory;
|
||||
$this->smartFileSystem = $smartFileSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $templateVariables
|
||||
* @return string[]
|
||||
*/
|
||||
public function generateFiles(
|
||||
array $templateFileInfos,
|
||||
array $templateVariables,
|
||||
Configuration $configuration,
|
||||
string $destinationDirectory
|
||||
): array {
|
||||
$generatedFilePaths = [];
|
||||
|
||||
foreach ($templateFileInfos as $fileInfo) {
|
||||
$generatedFilePaths[] = $this->generateFileInfoWithTemplateVariables(
|
||||
$fileInfo,
|
||||
$templateVariables,
|
||||
$configuration->getPackage(),
|
||||
$destinationDirectory
|
||||
);
|
||||
}
|
||||
|
||||
return $generatedFilePaths;
|
||||
}
|
||||
|
||||
private function generateFileInfoWithTemplateVariables(
|
||||
SmartFileInfo $smartFileInfo,
|
||||
array $templateVariables,
|
||||
string $package,
|
||||
string $targetDirectory
|
||||
): string {
|
||||
$targetFilePath = $this->templateFileSystem->resolveDestination(
|
||||
$smartFileInfo,
|
||||
$templateVariables,
|
||||
$package,
|
||||
$targetDirectory
|
||||
);
|
||||
|
||||
$content = $this->templateFactory->create($smartFileInfo->getContents(), $templateVariables);
|
||||
$this->smartFileSystem->dumpFile($targetFilePath, $content);
|
||||
|
||||
return $targetFilePath;
|
||||
}
|
||||
}
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||
namespace Rector\RectorGenerator\Guard;
|
||||
|
||||
use Rector\RectorGenerator\FileSystem\TemplateFileSystem;
|
||||
use Rector\RectorGenerator\ValueObject\Configuration;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
|
@ -33,10 +32,11 @@ final class OverrideGuard
|
|||
public function isUnwantedOverride(
|
||||
array $templateFileInfos,
|
||||
array $templateVariables,
|
||||
Configuration $configuration
|
||||
string $package,
|
||||
string $targetDirectory
|
||||
): bool {
|
||||
foreach ($templateFileInfos as $templateFileInfo) {
|
||||
if (! $this->doesFileInfoAlreadyExist($templateVariables, $configuration, $templateFileInfo)) {
|
||||
if (! $this->doesFileInfoAlreadyExist($templateVariables, $package, $templateFileInfo, $targetDirectory)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -48,13 +48,15 @@ final class OverrideGuard
|
|||
|
||||
private function doesFileInfoAlreadyExist(
|
||||
array $templateVariables,
|
||||
Configuration $configuration,
|
||||
SmartFileInfo $templateFileInfo
|
||||
string $package,
|
||||
SmartFileInfo $templateFileInfo,
|
||||
string $targetDirectory
|
||||
): bool {
|
||||
$destination = $this->templateFileSystem->resolveDestination(
|
||||
$templateFileInfo,
|
||||
$templateVariables,
|
||||
$configuration
|
||||
$package,
|
||||
$targetDirectory
|
||||
);
|
||||
|
||||
return file_exists($destination);
|
||||
|
|
|
@ -5,17 +5,27 @@ declare(strict_types=1);
|
|||
namespace Rector\RectorGenerator\NodeFactory;
|
||||
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\ArrayDimFetch;
|
||||
use PhpParser\Node\Expr\BinaryOp\Coalesce;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Expr\Variable;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\ClassConst;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Expression;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\FloatType;
|
||||
use PHPStan\Type\IntegerType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Core\Exception\NotImplementedYetException;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Core\PhpParser\Node\NodeFactory;
|
||||
use Rector\Core\Util\StaticRectorStrings;
|
||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
||||
|
||||
final class ConfigurationNodeFactory
|
||||
{
|
||||
|
@ -24,9 +34,21 @@ final class ConfigurationNodeFactory
|
|||
*/
|
||||
private $nodeFactory;
|
||||
|
||||
public function __construct(NodeFactory $nodeFactory)
|
||||
{
|
||||
/**
|
||||
* @var PhpDocInfoFactory
|
||||
*/
|
||||
private $phpDocInfoFactory;
|
||||
|
||||
public function __construct(
|
||||
NodeFactory $nodeFactory,
|
||||
ParameterProvider $parameterProvider,
|
||||
PhpDocInfoFactory $phpDocInfoFactory
|
||||
) {
|
||||
$this->nodeFactory = $nodeFactory;
|
||||
$this->phpDocInfoFactory = $phpDocInfoFactory;
|
||||
|
||||
// so types are PHP 7.2 compatible
|
||||
$parameterProvider->changeParameter(Option::PHP_VERSION_FEATURES, PhpVersionFeature::BEFORE_TYPED_PROPERTIES);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,10 +58,13 @@ final class ConfigurationNodeFactory
|
|||
public function createProperties(array $ruleConfiguration): array
|
||||
{
|
||||
$properties = [];
|
||||
foreach (array_keys($ruleConfiguration) as $variable) {
|
||||
$variable = ltrim($variable, '$');
|
||||
foreach (array_keys($ruleConfiguration) as $constantName) {
|
||||
$propertyName = StaticRectorStrings::uppercaseUnderscoreToPascalCase($constantName);
|
||||
$type = new ArrayType(new MixedType(), new MixedType());
|
||||
$properties[] = $this->nodeFactory->createPrivatePropertyFromNameAndType($variable, $type);
|
||||
|
||||
$property = $this->nodeFactory->createPrivatePropertyFromNameAndType($propertyName, $type);
|
||||
$property->props[0]->default = new Array_([]);
|
||||
$properties[] = $property;
|
||||
}
|
||||
|
||||
return $properties;
|
||||
|
@ -47,57 +72,63 @@ final class ConfigurationNodeFactory
|
|||
|
||||
/**
|
||||
* @param array<string, mixed> $ruleConfiguration
|
||||
* @return ClassConst[]
|
||||
*/
|
||||
public function createConstructorClassMethod(array $ruleConfiguration): ClassMethod
|
||||
public function createConfigurationConstants(array $ruleConfiguration): array
|
||||
{
|
||||
$classMethod = $this->nodeFactory->createPublicMethod('__construct');
|
||||
$classConsts = [];
|
||||
|
||||
$assigns = [];
|
||||
$params = [];
|
||||
|
||||
foreach ($ruleConfiguration as $variable => $values) {
|
||||
$variable = ltrim($variable, '$');
|
||||
|
||||
$assign = $this->nodeFactory->createPropertyAssignment($variable);
|
||||
$assigns[] = new Expression($assign);
|
||||
|
||||
$type = $this->resolveParamType($values);
|
||||
$param = $this->nodeFactory->createParamFromNameAndType($variable, $type);
|
||||
if ($type instanceof ArrayType) {
|
||||
// add default for fast testing property set mgaic purposes - @todo refactor for cleaner way later
|
||||
$param->default = new Array_([]);
|
||||
}
|
||||
|
||||
$params[] = $param;
|
||||
foreach (array_keys($ruleConfiguration) as $constantName) {
|
||||
$constantValue = strtolower($constantName);
|
||||
$classConst = $this->nodeFactory->createPublicClassConst($constantName, $constantValue);
|
||||
$classConsts[] = $classConst;
|
||||
}
|
||||
|
||||
return $classConsts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $ruleConfiguration
|
||||
*/
|
||||
public function createConfigureClassMethod(array $ruleConfiguration): ClassMethod
|
||||
{
|
||||
$classMethod = $this->nodeFactory->createPublicMethod('configure');
|
||||
$classMethod->returnType = new Identifier('void');
|
||||
|
||||
$configurationVariable = new Variable('configuration');
|
||||
$configurationParam = new Param($configurationVariable);
|
||||
$configurationParam->type = new Identifier('array');
|
||||
$classMethod->params[] = $configurationParam;
|
||||
|
||||
$assigns = [];
|
||||
foreach (array_keys($ruleConfiguration) as $constantName) {
|
||||
$coalesce = $this->createConstantInConfigurationCoalesce($constantName, $configurationVariable);
|
||||
|
||||
$propertyName = StaticRectorStrings::uppercaseUnderscoreToPascalCase($constantName);
|
||||
$assign = $this->nodeFactory->createPropertyAssignmentWithExpr($propertyName, $coalesce);
|
||||
$assigns[] = new Expression($assign);
|
||||
}
|
||||
|
||||
$classMethod->params = $params;
|
||||
$classMethod->stmts = $assigns;
|
||||
|
||||
$phpDocInfo = $this->phpDocInfoFactory->createEmpty($classMethod);
|
||||
|
||||
$identifierTypeNode = new IdentifierTypeNode('mixed[]');
|
||||
$paramTagValueNode = new ParamTagValueNode($identifierTypeNode, false, '$configuration', '');
|
||||
$phpDocInfo->addTagValueNode($paramTagValueNode);
|
||||
|
||||
return $classMethod;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $value
|
||||
*/
|
||||
private function resolveParamType($value): Type
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return new ArrayType(new MixedType(), new MixedType());
|
||||
}
|
||||
private function createConstantInConfigurationCoalesce(
|
||||
string $constantName,
|
||||
Variable $configurationVariable
|
||||
): Coalesce {
|
||||
$classConstFetch = new ClassConstFetch(new Name('self'), $constantName);
|
||||
$arrayDimFetch = new ArrayDimFetch($configurationVariable, $classConstFetch);
|
||||
|
||||
if (is_string($value)) {
|
||||
return new StringType();
|
||||
}
|
||||
$emptyArray = new Array_([]);
|
||||
|
||||
if (is_int($value)) {
|
||||
return new IntegerType();
|
||||
}
|
||||
|
||||
if (is_float($value)) {
|
||||
return new FloatType();
|
||||
}
|
||||
|
||||
throw new NotImplementedYetException();
|
||||
return new Coalesce($arrayDimFetch, $emptyArray);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,34 @@ declare(strict_types=1);
|
|||
namespace Rector\RectorGenerator;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node\Expr\Array_;
|
||||
use PhpParser\Node\Expr\ArrayItem;
|
||||
use PhpParser\Node\Expr\ClassConstFetch;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
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\Configuration;
|
||||
|
||||
final class TemplateVariablesFactory
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const SELF = 'self';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const VARIABLE_PACKAGE = '__Package__';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const VARIABLE_PACKAGE_LOWERCASE = '__package__';
|
||||
|
||||
/**
|
||||
* @var BetterStandardPrinter
|
||||
*/
|
||||
|
@ -27,24 +48,31 @@ final class TemplateVariablesFactory
|
|||
*/
|
||||
private $configurationNodeFactory;
|
||||
|
||||
/**
|
||||
* @var TemplateFactory
|
||||
*/
|
||||
private $templateFactory;
|
||||
|
||||
public function __construct(
|
||||
BetterStandardPrinter $betterStandardPrinter,
|
||||
ConfigurationNodeFactory $configurationNodeFactory,
|
||||
NodeFactory $nodeFactory
|
||||
NodeFactory $nodeFactory,
|
||||
TemplateFactory $templateFactory
|
||||
) {
|
||||
$this->betterStandardPrinter = $betterStandardPrinter;
|
||||
$this->nodeFactory = $nodeFactory;
|
||||
$this->configurationNodeFactory = $configurationNodeFactory;
|
||||
$this->templateFactory = $templateFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed[]
|
||||
* @return string[]
|
||||
*/
|
||||
public function createFromConfiguration(Configuration $configuration): array
|
||||
{
|
||||
$data = [
|
||||
'__Package__' => $configuration->getPackage(),
|
||||
'__package__' => $configuration->getPackageDirectory(),
|
||||
self::VARIABLE_PACKAGE => $configuration->getPackage(),
|
||||
self::VARIABLE_PACKAGE_LOWERCASE => $configuration->getPackageDirectory(),
|
||||
'__Category__' => $configuration->getCategory(),
|
||||
'__Description__' => $configuration->getDescription(),
|
||||
'__Name__' => $configuration->getName(),
|
||||
|
@ -55,13 +83,28 @@ final class TemplateVariablesFactory
|
|||
'__Source__' => $this->createSourceDocBlock($configuration->getSource()),
|
||||
];
|
||||
|
||||
$rectorClass = $this->templateFactory->create(ConfigFilesystem::RECTOR_FQN_NAME_PATTERN, $data);
|
||||
$data['__RectorClass_'] = $rectorClass;
|
||||
|
||||
if ($configuration->getRuleConfiguration() !== []) {
|
||||
$data['__RuleConfiguration__'] = $this->createRuleConfiguration($configuration->getRuleConfiguration());
|
||||
$data['__ConfigurationProperty__'] = $this->createConfigurationProperty(
|
||||
$data['__TestRuleConfiguration__'] = $this->createRuleConfiguration(
|
||||
$data['__RectorClass_'],
|
||||
$configuration->getRuleConfiguration()
|
||||
);
|
||||
$data['__RuleConfiguration__'] = $this->createRuleConfiguration(
|
||||
self::SELF,
|
||||
$configuration->getRuleConfiguration()
|
||||
);
|
||||
|
||||
$data['__ConfigurationConstructor__'] = $this->createConfigurationConstructor(
|
||||
$data['__ConfigurationProperties__'] = $this->createConfigurationProperty(
|
||||
$configuration->getRuleConfiguration()
|
||||
);
|
||||
|
||||
$data['__ConfigurationConstants__'] = $this->createConfigurationConstants(
|
||||
$configuration->getRuleConfiguration()
|
||||
);
|
||||
|
||||
$data['__ConfigureClassMethod__'] = $this->createConfigureClassMethod(
|
||||
$configuration->getRuleConfiguration()
|
||||
);
|
||||
}
|
||||
|
@ -74,8 +117,8 @@ final class TemplateVariablesFactory
|
|||
);
|
||||
}
|
||||
|
||||
$data['__NodeTypes_Php__'] = $this->createNodeTypePhp($configuration);
|
||||
$data['__NodeTypes_Doc__'] = '\\' . implode('|\\', $configuration->getNodeTypes());
|
||||
$data['__NodeTypesPhp__'] = $this->createNodeTypePhp($configuration);
|
||||
$data['__NodeTypesDoc__'] = '\\' . implode('|\\', $configuration->getNodeTypes());
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -124,9 +167,20 @@ final class TemplateVariablesFactory
|
|||
/**
|
||||
* @param mixed[] $configuration
|
||||
*/
|
||||
private function createRuleConfiguration(array $configuration): string
|
||||
private function createRuleConfiguration(string $rectorClass, array $configuration): string
|
||||
{
|
||||
$array = $this->nodeFactory->createArray($configuration);
|
||||
$arrayItems = [];
|
||||
foreach ($configuration as $constantName => $variableConfiguration) {
|
||||
if ($rectorClass === self::SELF) {
|
||||
$class = new Name(self::SELF);
|
||||
} else {
|
||||
$class = new FullyQualified($rectorClass);
|
||||
}
|
||||
$classConstFetch = new ClassConstFetch($class, $constantName);
|
||||
$arrayItems[] = new ArrayItem($this->nodeFactory->createArray($variableConfiguration), $classConstFetch);
|
||||
}
|
||||
|
||||
$array = new Array_($arrayItems);
|
||||
return $this->betterStandardPrinter->print($array);
|
||||
}
|
||||
|
||||
|
@ -142,9 +196,18 @@ final class TemplateVariablesFactory
|
|||
/**
|
||||
* @param array<string, mixed> $ruleConfiguration
|
||||
*/
|
||||
private function createConfigurationConstructor(array $ruleConfiguration): string
|
||||
private function createConfigureClassMethod(array $ruleConfiguration): string
|
||||
{
|
||||
$classMethod = $this->configurationNodeFactory->createConstructorClassMethod($ruleConfiguration);
|
||||
$classMethod = $this->configurationNodeFactory->createConfigureClassMethod($ruleConfiguration);
|
||||
return $this->betterStandardPrinter->print($classMethod);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $ruleConfiguration
|
||||
*/
|
||||
private function createConfigurationConstants(array $ruleConfiguration): string
|
||||
{
|
||||
$configurationConstants = $this->configurationNodeFactory->createConfigurationConstants($ruleConfiguration);
|
||||
return $this->betterStandardPrinter->print($configurationConstants);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Rector\RectorGenerator\ValueObject;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Util\StaticRectorStrings;
|
||||
use Symplify\SetConfigResolver\ValueObject\Set;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
@ -201,7 +202,8 @@ final class Configuration
|
|||
private function setName(string $name): void
|
||||
{
|
||||
if (! Strings::endsWith($name, 'Rector')) {
|
||||
$name .= 'Rector';
|
||||
$message = sprintf('Rector name "%s" must end with "Rector"', $name);
|
||||
throw new ShouldNotHappenException($message);
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Rector\__Package__\Rector\__Category__;
|
||||
|
||||
use PhpParser\Node;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
|
@ -13,11 +14,11 @@ use Rector\Core\RectorDefinition\RectorDefinition;
|
|||
__Source__
|
||||
* @see \Rector\__Package__\Tests\Rector\__Category__\__Name__\__Name__Test
|
||||
*/
|
||||
final class __Name__ extends AbstractRector
|
||||
final class __Name__ extends AbstractRector implements ConfigurableRectorInterface
|
||||
{
|
||||
__ConfigurationProperty__
|
||||
__ConfigurationConstants__
|
||||
|
||||
__ConfigurationConstructor__
|
||||
__ConfigurationProperties__
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
|
@ -35,11 +36,11 @@ final class __Name__ extends AbstractRector
|
|||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return __NodeTypes_Php__;
|
||||
return __NodeTypesPhp__;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param __NodeTypes_Doc__ $node
|
||||
* @param __NodeTypesDoc__ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
|
@ -47,4 +48,6 @@ final class __Name__ extends AbstractRector
|
|||
|
||||
return $node;
|
||||
}
|
||||
|
||||
__ConfigureClassMethod__
|
||||
}
|
||||
|
|
|
@ -30,11 +30,11 @@ final class __Name__ extends AbstractRector
|
|||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return __NodeTypes_Php__;
|
||||
return __NodeTypesPhp__;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param __NodeTypes_Doc__ $node
|
||||
* @param __NodeTypesDoc__ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
|
|
|
@ -26,7 +26,7 @@ final class __Name__ExtraTest extends AbstractRectorTestCase
|
|||
{
|
||||
return [
|
||||
\Rector\__Package__\Rector\__Category__\__Name__::class =>
|
||||
__RuleConfiguration__
|
||||
__TestRuleConfiguration__
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ final class __Name__Test extends AbstractRectorTestCase
|
|||
{
|
||||
return [
|
||||
\Rector\__Package__\Rector\__Category__\__Name__::class =>
|
||||
__RuleConfiguration__
|
||||
__TestRuleConfiguration__
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\RectorGenerator\Tests\PHPUnit\Behavior;
|
||||
|
||||
use Rector\RectorGenerator\Tests\PHPUnit\ValueObject\ExpectedAndOutputFileInfoPair;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
use Symplify\SmartFileSystem\Finder\FinderSanitizer;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
/**
|
||||
* Use only in "\PHPUnit\Framework\TestCase"
|
||||
*
|
||||
* Answer here
|
||||
* @see https://stackoverflow.com/questions/54263109/how-to-assert-2-directories-are-identical-in-phpunit
|
||||
*/
|
||||
trait DirectoryAssertableTrait
|
||||
{
|
||||
protected function assertDirectoryEquals(string $expectedDirectory, string $outputDirectory): void
|
||||
{
|
||||
$expectedFileInfos = $this->findFileInfosInDirectory($expectedDirectory);
|
||||
$outputFileInfos = $this->findFileInfosInDirectory($outputDirectory);
|
||||
|
||||
$fileInfosByRelativeFilePath = $this->groupFileInfosByRelativeFilePath(
|
||||
$expectedFileInfos,
|
||||
$expectedDirectory,
|
||||
$outputFileInfos,
|
||||
$outputDirectory
|
||||
);
|
||||
|
||||
foreach ($fileInfosByRelativeFilePath as $relativeFilePath => $expectedAndOutputFileInfoPair) {
|
||||
// output file exists
|
||||
$this->assertFileExists($outputDirectory . '/' . $relativeFilePath);
|
||||
|
||||
if (! $expectedAndOutputFileInfoPair->doesOutputFileExist()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// they have the same content
|
||||
$this->assertSame(
|
||||
$expectedAndOutputFileInfoPair->getExpectedFileContent(),
|
||||
$expectedAndOutputFileInfoPair->getOutputFileContent(),
|
||||
$relativeFilePath
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SmartFileInfo[]
|
||||
*/
|
||||
private function findFileInfosInDirectory(string $directory): array
|
||||
{
|
||||
$firstDirectoryFinder = new Finder();
|
||||
$firstDirectoryFinder->files()
|
||||
->in($directory);
|
||||
|
||||
$finderSanitizer = new FinderSanitizer();
|
||||
return $finderSanitizer->sanitize($firstDirectoryFinder);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SmartFileInfo[] $expectedFileInfos
|
||||
* @param SmartFileInfo[] $outputFileInfos
|
||||
* @return ExpectedAndOutputFileInfoPair[]
|
||||
*/
|
||||
private function groupFileInfosByRelativeFilePath(
|
||||
array $expectedFileInfos,
|
||||
string $expectedDirectory,
|
||||
array $outputFileInfos,
|
||||
string $outputDirectory
|
||||
): array {
|
||||
$fileInfosByRelativeFilePath = [];
|
||||
|
||||
foreach ($expectedFileInfos as $expectedFileInfo) {
|
||||
$relativeFilePath = $expectedFileInfo->getRelativeFilePathFromDirectory($expectedDirectory);
|
||||
|
||||
// match output file info
|
||||
$outputFileInfo = $this->resolveFileInfoByRelativeFilePath(
|
||||
$outputFileInfos,
|
||||
$outputDirectory,
|
||||
$relativeFilePath
|
||||
);
|
||||
|
||||
$fileInfosByRelativeFilePath[$relativeFilePath] = new ExpectedAndOutputFileInfoPair(
|
||||
$expectedFileInfo,
|
||||
$outputFileInfo
|
||||
);
|
||||
}
|
||||
|
||||
return $fileInfosByRelativeFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SmartFileInfo[] $fileInfos
|
||||
*/
|
||||
private function resolveFileInfoByRelativeFilePath(
|
||||
array $fileInfos,
|
||||
string $directory,
|
||||
string $desiredRelativeFilePath
|
||||
): ?SmartFileInfo {
|
||||
foreach ($fileInfos as $fileInfo) {
|
||||
$relativeFilePath = $fileInfo->getRelativeFilePathFromDirectory($directory);
|
||||
if ($desiredRelativeFilePath !== $relativeFilePath) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $fileInfo;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\RectorGenerator\Tests\PHPUnit\ValueObject;
|
||||
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class ExpectedAndOutputFileInfoPair
|
||||
{
|
||||
/**
|
||||
* @var SmartFileInfo
|
||||
*/
|
||||
private $expectedFileInfo;
|
||||
|
||||
/**
|
||||
* @var SmartFileInfo|null
|
||||
*/
|
||||
private $outputFileInfo;
|
||||
|
||||
public function __construct(SmartFileInfo $expectedFileInfo, ?SmartFileInfo $outputFileInfo)
|
||||
{
|
||||
$this->expectedFileInfo = $expectedFileInfo;
|
||||
$this->outputFileInfo = $outputFileInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @noRector \Rector\Privatization\Rector\ClassMethod\PrivatizeLocalOnlyMethodRector
|
||||
*/
|
||||
public function getExpectedFileContent(): string
|
||||
{
|
||||
return $this->expectedFileInfo->getContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @noRector \Rector\Privatization\Rector\ClassMethod\PrivatizeLocalOnlyMethodRector
|
||||
*/
|
||||
public function getOutputFileContent(): string
|
||||
{
|
||||
if ($this->outputFileInfo === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
return $this->outputFileInfo->getContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* @noRector \Rector\Privatization\Rector\ClassMethod\PrivatizeLocalOnlyMethodRector
|
||||
*/
|
||||
public function doesOutputFileExist(): bool
|
||||
{
|
||||
return $this->outputFileInfo !== null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\Rector\MethodCall;
|
||||
|
||||
use PhpParser\Node;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
|
||||
/**
|
||||
|
||||
* @see \Rector\Symfony\Tests\Rector\MethodCall\ChangeServiceArgumentsToMethodCallRector\ChangeServiceArgumentsToMethodCallRectorTest
|
||||
*/
|
||||
final class ChangeServiceArgumentsToMethodCallRector extends AbstractRector implements ConfigurableRectorInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const CLASS_TYPE_TO_METHOD_NAME = 'class_type_to_method_name';
|
||||
|
||||
/**
|
||||
* @var mixed[]
|
||||
*/
|
||||
private $classTypeToMethodName = [];
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Change $service->arg(...) to $service->call(...)', [
|
||||
new ConfiguredCodeSample(
|
||||
<<<'PHP'
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(SomeClass::class)
|
||||
->arg('$key', 'value');
|
||||
}
|
||||
PHP
|
||||
,
|
||||
<<<'PHP'
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(SomeClass::class)
|
||||
->call('configure', [[
|
||||
'$key' => 'value'
|
||||
]]);
|
||||
}
|
||||
PHP
|
||||
,
|
||||
[self::CLASS_TYPE_TO_METHOD_NAME => ['SomeClass' => 'configure']]
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [\PhpParser\Node\Expr\MethodCall::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PhpParser\Node\Expr\MethodCall $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
// change the node
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$this->classTypeToMethodName = $configuration[self::CLASS_TYPE_TO_METHOD_NAME] ?? [];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Symfony\Tests\Rector\MethodCall\ChangeServiceArgumentsToMethodCallRector;
|
||||
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class ChangeServiceArgumentsToMethodCallRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(\Symplify\SmartFileSystem\SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
public function provideData(): \Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorsWithConfiguration(): array
|
||||
{
|
||||
return [
|
||||
\Rector\Symfony\Rector\MethodCall\ChangeServiceArgumentsToMethodCallRector::class =>
|
||||
[\Rector\Symfony\Rector\MethodCall\ChangeServiceArgumentsToMethodCallRector::CLASS_TYPE_TO_METHOD_NAME => ['SomeClass' => 'configure']]
|
||||
];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Symfony\Tests\Rector\MethodCall\ChangeServiceArgumentsToMethodCallRector\Fixture;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(SomeClass::class)
|
||||
->arg('$key', 'value');
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Symfony\Tests\Rector\MethodCall\ChangeServiceArgumentsToMethodCallRector\Fixture;
|
||||
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(SomeClass::class)
|
||||
->call('configure', [[
|
||||
'$key' => 'value'
|
||||
]]);
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
|
||||
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\PHPUnit\Behavior\DirectoryAssertableTrait;
|
||||
use Rector\RectorGenerator\Tests\RectorGenerator\Source\StaticRectorRecipeFactory;
|
||||
use Rector\RectorGenerator\ValueObject\Configuration;
|
||||
use Symplify\PackageBuilder\Tests\AbstractKernelTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileSystem;
|
||||
|
||||
final class RectorGeneratorTest extends AbstractKernelTestCase
|
||||
{
|
||||
use DirectoryAssertableTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private const DESTINATION_DIRECTORY = __DIR__ . '/__temp';
|
||||
|
||||
/**
|
||||
* @var ConfigurationFactory
|
||||
*/
|
||||
private $configurationFactory;
|
||||
|
||||
/**
|
||||
* @var TemplateVariablesFactory
|
||||
*/
|
||||
private $templateVariablesFactory;
|
||||
|
||||
/**
|
||||
* @var TemplateFinder
|
||||
*/
|
||||
private $templateFinder;
|
||||
|
||||
/**
|
||||
* @var FileGenerator
|
||||
*/
|
||||
private $fileGenerator;
|
||||
|
||||
/**
|
||||
* @var SmartFileSystem
|
||||
*/
|
||||
private $smartFileSystem;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$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);
|
||||
|
||||
$this->smartFileSystem = self::$container->get(SmartFileSystem::class);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
// cleanup temporary data
|
||||
$this->smartFileSystem->remove(self::DESTINATION_DIRECTORY);
|
||||
}
|
||||
|
||||
public function test(): void
|
||||
{
|
||||
$configuration = $this->createConfiguration();
|
||||
$templateFileInfos = $this->templateFinder->find($configuration);
|
||||
$templateVariables = $this->templateVariablesFactory->createFromConfiguration($configuration);
|
||||
|
||||
$this->fileGenerator->generateFiles(
|
||||
$templateFileInfos,
|
||||
$templateVariables,
|
||||
$configuration,
|
||||
self::DESTINATION_DIRECTORY
|
||||
);
|
||||
|
||||
// @todo decouple to EasyTesting
|
||||
$this->assertDirectoryEquals(__DIR__ . '/Fixture/expected', self::DESTINATION_DIRECTORY);
|
||||
}
|
||||
|
||||
private function createConfiguration(): Configuration
|
||||
{
|
||||
$rectorRecipe = StaticRectorRecipeFactory::createWithConfiguration();
|
||||
|
||||
return $this->configurationFactory->createFromRectorRecipe($rectorRecipe);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\RectorGenerator\Tests\RectorGenerator\Source;
|
||||
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use Rector\RectorGenerator\ValueObject\RecipeOption;
|
||||
|
||||
final class StaticRectorRecipeFactory
|
||||
{
|
||||
public static function createWithConfiguration(): array
|
||||
{
|
||||
return [
|
||||
RecipeOption::PACKAGE => 'Symfony',
|
||||
RecipeOption::NAME => 'ChangeServiceArgumentsToMethodCallRector',
|
||||
RecipeOption::NODE_TYPES => [
|
||||
MethodCall::class,
|
||||
],
|
||||
RecipeOption::DESCRIPTION => 'Change $service->arg(...) to $service->call(...)',
|
||||
RecipeOption::CODE_BEFORE => <<<'CODE_SAMPLE'
|
||||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(SomeClass::class)
|
||||
->arg('$key', 'value');
|
||||
}
|
||||
CODE_SAMPLE,
|
||||
RecipeOption::CODE_AFTER => <<<'CODE_SAMPLE'
|
||||
<?php
|
||||
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(SomeClass::class)
|
||||
->call('configure', [[
|
||||
'$key' => 'value'
|
||||
]]);
|
||||
}
|
||||
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,
|
||||
|
||||
// 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,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
<directory>packages/*/tests</directory>
|
||||
<directory>tests</directory>
|
||||
<directory>utils/*/tests</directory>
|
||||
<exclude>packages/rector-generator/templates</exclude>
|
||||
<exclude>packages/rector-generator/tests/RectorGenerator/Fixture</exclude>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ final class RectorsFinder
|
|||
|
||||
$robotLoader->setTempDirectory(sys_get_temp_dir() . '/_rector_finder');
|
||||
$robotLoader->acceptFiles = [$name];
|
||||
$robotLoader->excludeDirectory(__DIR__ . '/../../../packages/rector-generator/tests');
|
||||
$robotLoader->rebuild();
|
||||
|
||||
return array_keys($robotLoader->getIndexedClasses());
|
||||
|
|
|
@ -57,6 +57,12 @@ final class StaticRectorStrings
|
|||
return lcfirst($string);
|
||||
}
|
||||
|
||||
public static function uppercaseUnderscoreToPascalCase(string $input): string
|
||||
{
|
||||
$input = strtolower($input);
|
||||
return self::underscoreToPascalCase($input);
|
||||
}
|
||||
|
||||
public static function underscoreToCamelCase(string $input): string
|
||||
{
|
||||
$nameParts = explode('_', $input);
|
||||
|
|
|
@ -136,6 +136,11 @@ final class PhpVersionFeature
|
|||
*/
|
||||
public const BEFORE_UNION_TYPES = '7.4';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const BEFORE_TYPED_PROPERTIES = '7.3';
|
||||
|
||||
/**
|
||||
* @see https://wiki.php.net/rfc/covariant-returns-and-contravariant-parameters
|
||||
* @var string
|
||||
|
|
|
@ -77,6 +77,7 @@ final class ValidateFixtureSuffixCommand extends Command
|
|||
->notPath('#TagValueNodeReprint#')
|
||||
->notPath('#PhpSpecToPHPUnitRector#')
|
||||
->notPath('#Source#')
|
||||
->notPath('#expected#')
|
||||
->notPath('DoctrineRepositoryAsService/Fixture/PostController.php')
|
||||
->notPath('Namespace_/ImportFullyQualifiedNamesRector/Fixture/SharedShortName.php')
|
||||
->notPath('Class_/RenameClassRector/Fixture/DuplicatedClass.php')
|
||||
|
|
Loading…
Reference in New Issue
Block a user