Updated Rector to commit 912d0b7ecc78aae4cbc2f3df7992fab9b5c63014

912d0b7ecc [PhpParser] Remove regex check single line spaces before <?php on FileProcessor (#6525)
This commit is contained in:
Tomas Votruba 2024-12-01 15:02:31 +00:00
parent 6ca3d0b122
commit cce6781c15
15 changed files with 370 additions and 62 deletions

View File

@ -4,7 +4,6 @@ declare (strict_types=1);
namespace Rector\Application;
use RectorPrefix202412\Nette\Utils\FileSystem;
use RectorPrefix202412\Nette\Utils\Strings;
use PHPStan\AnalysedCodeException;
use PHPStan\Parser\ParserErrorsException;
use Rector\Caching\Detector\ChangedFilesDetector;
@ -23,7 +22,6 @@ use Rector\ValueObject\Application\File;
use Rector\ValueObject\Configuration;
use Rector\ValueObject\Error\SystemError;
use Rector\ValueObject\FileProcessResult;
use Rector\ValueObject\Reporting\FileDiff;
use RectorPrefix202412\Symfony\Component\Console\Style\SymfonyStyle;
use Throwable;
final class FileProcessor
@ -68,11 +66,6 @@ final class FileProcessor
* @readonly
*/
private NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator;
/**
* @var string
* @see https://regex101.com/r/llm7XZ/1
*/
private const OPEN_TAG_SPACED_REGEX = '#^[ \\t]+<\\?php#m';
public function __construct(BetterStandardPrinter $betterStandardPrinter, RectorNodeTraverser $rectorNodeTraverser, SymfonyStyle $symfonyStyle, FileDiffFactory $fileDiffFactory, ChangedFilesDetector $changedFilesDetector, ErrorFactory $errorFactory, FilePathHelper $filePathHelper, PostFileProcessor $postFileProcessor, RectorParser $rectorParser, NodeScopeAndMetadataDecorator $nodeScopeAndMetadataDecorator)
{
$this->betterStandardPrinter = $betterStandardPrinter;
@ -157,26 +150,6 @@ final class FileProcessor
{
// only save to string first, no need to print to file when not needed
$newContent = $this->betterStandardPrinter->printFormatPreserving($file->getNewStmts(), $file->getOldStmts(), $file->getOldTokens());
/**
* When no diff applied, the PostRector may still change the content, that's why printing still needed
* On printing, the space may be wiped, these below check compare with original file content used to verify
* that no change actually needed
*/
if (!$file->getFileDiff() instanceof FileDiff) {
/**
* exact compare with original file content
*/
$originalFileContent = $file->getOriginalFileContent();
if ($originalFileContent === $newContent) {
return;
}
// handle space before <?php
$strippedNewContent = Strings::replace($newContent, self::OPEN_TAG_SPACED_REGEX, '<?php');
$strippedOriginalFileContent = Strings::replace($originalFileContent, self::OPEN_TAG_SPACED_REGEX, '<?php');
if ($strippedOriginalFileContent === $strippedNewContent) {
return;
}
}
// change file content early to make $file->hasChanged() based on new content
$file->changeFileContent($newContent);
if ($configuration->isDryRun()) {

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '1b68ff48b80d81bd4295ef304196fd97d74e0032';
public const PACKAGE_VERSION = '912d0b7ecc78aae4cbc2f3df7992fab9b5c63014';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2024-12-01 00:40:46';
public const RELEASE_DATE = '2024-12-01 22:00:05';
/**
* @var int
*/

View File

@ -22,6 +22,7 @@ use PhpParser\Node\Scalar\Int_;
use PhpParser\Node\Scalar\InterpolatedString;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Declare_;
use PhpParser\Node\Stmt\InlineHTML;
use PhpParser\Node\Stmt\Nop;
use PhpParser\PrettyPrinter\Standard;
use PHPStan\Node\Expr\AlwaysRememberedExpr;
@ -343,12 +344,18 @@ final class BetterStandardPrinter extends Standard
*/
private function containsNop(array $nodes) : bool
{
$hasNop = \false;
foreach ($nodes as $node) {
// early false when visited Node is InlineHTML
if ($node instanceof InlineHTML) {
return \false;
}
// use flag to avoid next is InlineHTML that returns early
if ($node instanceof Nop) {
return \true;
$hasNop = \true;
}
}
return \false;
return $hasNop;
}
private function wrapValueWith(String_ $string, string $wrap) : string
{

View File

@ -2236,6 +2236,9 @@ return array(
'Rector\\Symfony\\Contract\\Tag\\TagInterface' => $vendorDir . '/rector/rector-symfony/src/Contract/Tag/TagInterface.php',
'Rector\\Symfony\\DataProvider\\ServiceMapProvider' => $vendorDir . '/rector/rector-symfony/src/DataProvider/ServiceMapProvider.php',
'Rector\\Symfony\\DataProvider\\ServiceNameToTypeUniqueProvider' => $vendorDir . '/rector/rector-symfony/src/DataProvider/ServiceNameToTypeUniqueProvider.php',
'Rector\\Symfony\\DependencyInjection\\NodeDecorator\\CommandConstructorDecorator' => $vendorDir . '/rector/rector-symfony/rules/DependencyInjection/NodeDecorator/CommandConstructorDecorator.php',
'Rector\\Symfony\\DependencyInjection\\Rector\\Class_\\CommandGetByTypeToConstructorInjectionRector' => $vendorDir . '/rector/rector-symfony/rules/DependencyInjection/Rector/Class_/CommandGetByTypeToConstructorInjectionRector.php',
'Rector\\Symfony\\DependencyInjection\\Rector\\Class_\\ControllerGetByTypeToConstructorInjectionRector' => $vendorDir . '/rector/rector-symfony/rules/DependencyInjection/Rector/Class_/ControllerGetByTypeToConstructorInjectionRector.php',
'Rector\\Symfony\\DowngradeSymfony70\\Rector\\Class_\\DowngradeSymfonyCommandAttributeRector' => $vendorDir . '/rector/rector-symfony/rules/DowngradeSymfony70/Rector/Class_/DowngradeSymfonyCommandAttributeRector.php',
'Rector\\Symfony\\Enum\\SensioAttribute' => $vendorDir . '/rector/rector-symfony/src/Enum/SensioAttribute.php',
'Rector\\Symfony\\Enum\\SymfonyAnnotation' => $vendorDir . '/rector/rector-symfony/src/Enum/SymfonyAnnotation.php',

View File

@ -2455,6 +2455,9 @@ class ComposerStaticInitc12d7e0a7ec6f5f877903ca571cd9ab7
'Rector\\Symfony\\Contract\\Tag\\TagInterface' => __DIR__ . '/..' . '/rector/rector-symfony/src/Contract/Tag/TagInterface.php',
'Rector\\Symfony\\DataProvider\\ServiceMapProvider' => __DIR__ . '/..' . '/rector/rector-symfony/src/DataProvider/ServiceMapProvider.php',
'Rector\\Symfony\\DataProvider\\ServiceNameToTypeUniqueProvider' => __DIR__ . '/..' . '/rector/rector-symfony/src/DataProvider/ServiceNameToTypeUniqueProvider.php',
'Rector\\Symfony\\DependencyInjection\\NodeDecorator\\CommandConstructorDecorator' => __DIR__ . '/..' . '/rector/rector-symfony/rules/DependencyInjection/NodeDecorator/CommandConstructorDecorator.php',
'Rector\\Symfony\\DependencyInjection\\Rector\\Class_\\CommandGetByTypeToConstructorInjectionRector' => __DIR__ . '/..' . '/rector/rector-symfony/rules/DependencyInjection/Rector/Class_/CommandGetByTypeToConstructorInjectionRector.php',
'Rector\\Symfony\\DependencyInjection\\Rector\\Class_\\ControllerGetByTypeToConstructorInjectionRector' => __DIR__ . '/..' . '/rector/rector-symfony/rules/DependencyInjection/Rector/Class_/ControllerGetByTypeToConstructorInjectionRector.php',
'Rector\\Symfony\\DowngradeSymfony70\\Rector\\Class_\\DowngradeSymfonyCommandAttributeRector' => __DIR__ . '/..' . '/rector/rector-symfony/rules/DowngradeSymfony70/Rector/Class_/DowngradeSymfonyCommandAttributeRector.php',
'Rector\\Symfony\\Enum\\SensioAttribute' => __DIR__ . '/..' . '/rector/rector-symfony/src/Enum/SensioAttribute.php',
'Rector\\Symfony\\Enum\\SymfonyAnnotation' => __DIR__ . '/..' . '/rector/rector-symfony/src/Enum/SymfonyAnnotation.php',

View File

@ -1868,12 +1868,12 @@
"source": {
"type": "git",
"url": "https:\/\/github.com\/rectorphp\/rector-symfony.git",
"reference": "dfec6fd94c2cc2dc866b18c152aa7ecd45fd828e"
"reference": "617b3aff39d3ae14e5ea24ad5150fb4d0fc4a05e"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-symfony\/zipball\/dfec6fd94c2cc2dc866b18c152aa7ecd45fd828e",
"reference": "dfec6fd94c2cc2dc866b18c152aa7ecd45fd828e",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-symfony\/zipball\/617b3aff39d3ae14e5ea24ad5150fb4d0fc4a05e",
"reference": "617b3aff39d3ae14e5ea24ad5150fb4d0fc4a05e",
"shasum": ""
},
"require": {
@ -1898,7 +1898,7 @@
"tomasvotruba\/class-leak": "^1.0",
"tracy\/tracy": "^2.10"
},
"time": "2024-11-24T16:11:00+00:00",
"time": "2024-12-01T14:07:35+00:00",
"default-branch": true,
"type": "rector-extension",
"extra": {

File diff suppressed because one or more lines are too long

View File

@ -9,7 +9,7 @@ namespace Rector\RectorInstaller;
*/
final class GeneratedConfig
{
public const EXTENSIONS = array('rector/rector-doctrine' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-doctrine', 'relative_install_path' => '../../rector-doctrine', 'extra' => NULL, 'version' => 'dev-main 02ae401'), 'rector/rector-downgrade-php' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-downgrade-php', 'relative_install_path' => '../../rector-downgrade-php', 'extra' => NULL, 'version' => 'dev-main 7bb52fb'), 'rector/rector-phpunit' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-phpunit', 'relative_install_path' => '../../rector-phpunit', 'extra' => NULL, 'version' => 'dev-main a506b2c'), 'rector/rector-symfony' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-symfony', 'relative_install_path' => '../../rector-symfony', 'extra' => NULL, 'version' => 'dev-main dfec6fd'));
public const EXTENSIONS = array('rector/rector-doctrine' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-doctrine', 'relative_install_path' => '../../rector-doctrine', 'extra' => NULL, 'version' => 'dev-main 02ae401'), 'rector/rector-downgrade-php' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-downgrade-php', 'relative_install_path' => '../../rector-downgrade-php', 'extra' => NULL, 'version' => 'dev-main 7bb52fb'), 'rector/rector-phpunit' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-phpunit', 'relative_install_path' => '../../rector-phpunit', 'extra' => NULL, 'version' => 'dev-main a506b2c'), 'rector/rector-symfony' => array('install_path' => '/home/runner/work/rector-src/rector-src/rector-build/vendor/rector/rector-symfony', 'relative_install_path' => '../../rector-symfony', 'extra' => NULL, 'version' => 'dev-main 617b3af'));
private function __construct()
{
}

View File

@ -8,5 +8,13 @@ use Rector\Symfony\Symfony28\Rector\MethodCall\GetToConstructorInjectionRector;
use Rector\Symfony\Symfony34\Rector\Closure\ContainerGetNameToTypeInTestsRector;
use Rector\Symfony\Symfony42\Rector\MethodCall\ContainerGetToConstructorInjectionRector;
return static function (RectorConfig $rectorConfig) : void {
$rectorConfig->rules([ContainerGetToConstructorInjectionRector::class, ContainerGetNameToTypeInTestsRector::class, GetToConstructorInjectionRector::class]);
$rectorConfig->rules([
// modern step-by-step narrow approach
\Rector\Symfony\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector::class,
\Rector\Symfony\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector::class,
// legacy rules that require container fetch
ContainerGetToConstructorInjectionRector::class,
ContainerGetNameToTypeInTestsRector::class,
GetToConstructorInjectionRector::class,
]);
};

View File

@ -0,0 +1,37 @@
<?php
declare (strict_types=1);
namespace Rector\Symfony\DependencyInjection\NodeDecorator;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PHPStan\Type\ObjectType;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Rector\ValueObject\MethodName;
final class CommandConstructorDecorator
{
private NodeTypeResolver $nodeTypeResolver;
public function __construct(NodeTypeResolver $nodeTypeResolver)
{
$this->nodeTypeResolver = $nodeTypeResolver;
}
public function decorate(Class_ $class) : void
{
// special case for command to keep parent constructor call
if (!$this->nodeTypeResolver->isObjectType($class, new ObjectType('Symfony\\Component\\Console\\Command\\Command'))) {
return;
}
$constuctClassMethod = $class->getMethod(MethodName::CONSTRUCT);
if (!$constuctClassMethod instanceof ClassMethod) {
return;
}
// empty stmts? add parent::__construct() to setup command
if ((array) $constuctClassMethod->stmts === []) {
$parentConstructStaticCall = new StaticCall(new Name('parent'), '__construct');
$constuctClassMethod->stmts[] = new Expression($parentConstructStaticCall);
}
}
}

View File

@ -0,0 +1,145 @@
<?php
declare (strict_types=1);
namespace Rector\Symfony\DependencyInjection\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Reflection\ClassReflection;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeManipulator\ClassDependencyManipulator;
use Rector\PHPStan\ScopeFetcher;
use Rector\PostRector\ValueObject\PropertyMetadata;
use Rector\Rector\AbstractRector;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\Symfony\DependencyInjection\NodeDecorator\CommandConstructorDecorator;
use Rector\Symfony\Enum\SymfonyClass;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Symfony\Tests\DependencyInjection\Rector\Class_\CommandGetByTypeToConstructorInjectionRector\CommandGetByTypeToConstructorInjectionRectorTest
*/
final class CommandGetByTypeToConstructorInjectionRector extends AbstractRector
{
/**
* @readonly
*/
private ClassDependencyManipulator $classDependencyManipulator;
/**
* @readonly
*/
private PropertyNaming $propertyNaming;
/**
* @readonly
*/
private CommandConstructorDecorator $commandConstructorDecorator;
public function __construct(ClassDependencyManipulator $classDependencyManipulator, PropertyNaming $propertyNaming, CommandConstructorDecorator $commandConstructorDecorator)
{
$this->classDependencyManipulator = $classDependencyManipulator;
$this->propertyNaming = $propertyNaming;
$this->commandConstructorDecorator = $commandConstructorDecorator;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('From `$container->get(SomeType::class)` in commands to constructor injection (step 2/x)', [new CodeSample(<<<'CODE_SAMPLE'
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
final class SomeCommand extends ContainerAwareCommand
{
public function someMethod()
{
$someType = $this->get(SomeType::class);
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
final class SomeCommand extends ContainerAwareCommand
{
public function __construct(private SomeType $someType)
{
}
public function someMethod()
{
$someType = $this->someType;
}
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes() : array
{
return [Class_::class];
}
/**
* @param Class_ $node
*/
public function refactor(Node $node) : ?Node
{
if ($this->shouldSkipClass($node)) {
return null;
}
$propertyMetadatas = [];
$this->traverseNodesWithCallable($node, function (Node $node) use(&$propertyMetadatas) : ?Node {
if (!$node instanceof MethodCall) {
return null;
}
if ($node->isFirstClassCallable()) {
return null;
}
if (!$this->isName($node->name, 'get')) {
return null;
}
if (!$this->isName($node->var, 'this')) {
return null;
}
if (\count($node->getArgs()) !== 1) {
return null;
}
$firstArg = $node->getArgs()[0];
if (!$firstArg->value instanceof ClassConstFetch) {
return null;
}
// must be class const fetch
if (!$this->isName($firstArg->value->name, 'class')) {
return null;
}
$className = $this->getName($firstArg->value->class);
if (!\is_string($className)) {
return null;
}
$propertyName = $this->propertyNaming->fqnToVariableName($className);
$propertyMetadata = new PropertyMetadata($propertyName, new FullyQualifiedObjectType($className));
$propertyMetadatas[] = $propertyMetadata;
return $this->nodeFactory->createPropertyFetch('this', $propertyMetadata->getName());
});
if ($propertyMetadatas === []) {
return null;
}
foreach ($propertyMetadatas as $propertyMetadata) {
$this->classDependencyManipulator->addConstructorDependency($node, $propertyMetadata);
}
$this->commandConstructorDecorator->decorate($node);
return $node;
}
private function shouldSkipClass(Class_ $class) : bool
{
// keep it safe
if ($class->isAbstract()) {
return \true;
}
$scope = ScopeFetcher::fetch($class);
$classReflection = $scope->getClassReflection();
if (!$classReflection instanceof ClassReflection) {
return \true;
}
return !$classReflection->isSubclassOf(SymfonyClass::CONTAINER_AWARE_COMMAND);
}
}

View File

@ -0,0 +1,138 @@
<?php
declare (strict_types=1);
namespace Rector\Symfony\DependencyInjection\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Reflection\ClassReflection;
use Rector\Naming\Naming\PropertyNaming;
use Rector\NodeManipulator\ClassDependencyManipulator;
use Rector\PHPStan\ScopeFetcher;
use Rector\PostRector\ValueObject\PropertyMetadata;
use Rector\Rector\AbstractRector;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
use Rector\Symfony\Enum\SymfonyClass;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Symfony\Tests\DependencyInjection\Rector\Class_\ControllerGetByTypeToConstructorInjectionRector\ControllerGetByTypeToConstructorInjectionRectorTest
*/
final class ControllerGetByTypeToConstructorInjectionRector extends AbstractRector
{
/**
* @readonly
*/
private ClassDependencyManipulator $classDependencyManipulator;
/**
* @readonly
*/
private PropertyNaming $propertyNaming;
public function __construct(ClassDependencyManipulator $classDependencyManipulator, PropertyNaming $propertyNaming)
{
$this->classDependencyManipulator = $classDependencyManipulator;
$this->propertyNaming = $propertyNaming;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('From `$container->get(SomeType::class)` in controllers to constructor injection (step 1/x)', [new CodeSample(<<<'CODE_SAMPLE'
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
final class SomeCommand extends Controller
{
public function someMethod()
{
$someType = $this->get(SomeType::class);
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
final class SomeCommand extends Controller
{
public function __construct(private SomeType $someType)
{
}
public function someMethod()
{
$someType = $this->someType;
}
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes() : array
{
return [Class_::class];
}
/**
* @param Class_ $node
*/
public function refactor(Node $node) : ?Node
{
if ($this->shouldSkipClass($node)) {
return null;
}
$propertyMetadatas = [];
$this->traverseNodesWithCallable($node, function (Node $node) use(&$propertyMetadatas) : ?Node {
if (!$node instanceof MethodCall) {
return null;
}
if ($node->isFirstClassCallable()) {
return null;
}
if (!$this->isName($node->name, 'get')) {
return null;
}
if (!$this->isName($node->var, 'this')) {
return null;
}
if (\count($node->getArgs()) !== 1) {
return null;
}
$firstArg = $node->getArgs()[0];
if (!$firstArg->value instanceof ClassConstFetch) {
return null;
}
// must be class const fetch
if (!$this->isName($firstArg->value->name, 'class')) {
return null;
}
$className = $this->getName($firstArg->value->class);
if (!\is_string($className)) {
return null;
}
$propertyName = $this->propertyNaming->fqnToVariableName($className);
$propertyMetadata = new PropertyMetadata($propertyName, new FullyQualifiedObjectType($className));
$propertyMetadatas[] = $propertyMetadata;
return $this->nodeFactory->createPropertyFetch('this', $propertyMetadata->getName());
});
if ($propertyMetadatas === []) {
return null;
}
foreach ($propertyMetadatas as $propertyMetadata) {
$this->classDependencyManipulator->addConstructorDependency($node, $propertyMetadata);
}
return $node;
}
private function shouldSkipClass(Class_ $class) : bool
{
// keep it safe
if (!$class->isFinal()) {
return \true;
}
$scope = ScopeFetcher::fetch($class);
$classReflection = $scope->getClassReflection();
if (!$classReflection instanceof ClassReflection) {
return \true;
}
return !$classReflection->isSubclassOf(SymfonyClass::CONTROLLER);
}
}

View File

@ -6,18 +6,14 @@ namespace Rector\Symfony\Symfony42\Rector\MethodCall;
use PhpParser\Node;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PHPStan\Type\ObjectType;
use Rector\NodeManipulator\ClassDependencyManipulator;
use Rector\PHPUnit\NodeAnalyzer\TestsNodeAnalyzer;
use Rector\PostRector\ValueObject\PropertyMetadata;
use Rector\Rector\AbstractRector;
use Rector\Symfony\DependencyInjection\NodeDecorator\CommandConstructorDecorator;
use Rector\Symfony\NodeAnalyzer\DependencyInjectionMethodCallAnalyzer;
use Rector\ValueObject\MethodName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -39,11 +35,16 @@ final class ContainerGetToConstructorInjectionRector extends AbstractRector
* @readonly
*/
private ClassDependencyManipulator $classDependencyManipulator;
public function __construct(DependencyInjectionMethodCallAnalyzer $dependencyInjectionMethodCallAnalyzer, TestsNodeAnalyzer $testsNodeAnalyzer, ClassDependencyManipulator $classDependencyManipulator)
/**
* @readonly
*/
private CommandConstructorDecorator $commandConstructorDecorator;
public function __construct(DependencyInjectionMethodCallAnalyzer $dependencyInjectionMethodCallAnalyzer, TestsNodeAnalyzer $testsNodeAnalyzer, ClassDependencyManipulator $classDependencyManipulator, CommandConstructorDecorator $commandConstructorDecorator)
{
$this->dependencyInjectionMethodCallAnalyzer = $dependencyInjectionMethodCallAnalyzer;
$this->testsNodeAnalyzer = $testsNodeAnalyzer;
$this->classDependencyManipulator = $classDependencyManipulator;
$this->commandConstructorDecorator = $commandConstructorDecorator;
}
public function getRuleDefinition() : RuleDefinition
{
@ -123,23 +124,7 @@ CODE_SAMPLE
foreach ($propertyMetadatas as $propertyMetadata) {
$this->classDependencyManipulator->addConstructorDependency($class, $propertyMetadata);
}
$this->decorateCommandConstructor($class);
$this->commandConstructorDecorator->decorate($class);
return $node;
}
private function decorateCommandConstructor(Class_ $class) : void
{
// special case for command to keep parent constructor call
if (!$this->isObjectType($class, new ObjectType('Symfony\\Component\\Console\\Command\\Command'))) {
return;
}
$constuctClassMethod = $class->getMethod(MethodName::CONSTRUCT);
if (!$constuctClassMethod instanceof ClassMethod) {
return;
}
// empty stmts? add parent::__construct() to setup command
if ((array) $constuctClassMethod->stmts === []) {
$parentConstructStaticCall = new StaticCall(new Name('parent'), '__construct');
$constuctClassMethod->stmts[] = new Expression($parentConstructStaticCall);
}
}
}

View File

@ -5,6 +5,10 @@ namespace Rector\Symfony\Enum;
final class SymfonyClass
{
/**
* @var string
*/
public const CONTROLLER = 'Symfony\\Bundle\\FrameworkBundle\\Controller\\Controller';
/**
* @var string
*/
@ -13,4 +17,8 @@ final class SymfonyClass
* @var string
*/
public const COMMAND = 'Symfony\\Component\\Console\\Command\\Command';
/**
* @var string
*/
public const CONTAINER_AWARE_COMMAND = 'Symfony\\Bundle\\FrameworkBundle\\Command\\ContainerAwareCommand';
}

View File

@ -32,7 +32,8 @@ final class ServiceTypeMethodCallResolver
if (!isset($methodCall->args[0])) {
return new MixedType();
}
$argument = $methodCall->getArgs()[0]->value;
$firstArg = $methodCall->getArgs()[0];
$argument = $firstArg->value;
$serviceMap = $this->serviceMapProvider->provide();
if ($argument instanceof String_) {
return $serviceMap->getServiceType($argument->value);