Updated Rector to commit a61fbf265d2995453ad8d9f12b6bb00ff1937162

a61fbf265d Remove removeNode() from couple rules (#4083)
This commit is contained in:
Tomas Votruba 2023-06-05 13:42:47 +00:00
parent 1078587eae
commit bc7911e5d1
27 changed files with 284 additions and 648 deletions

View File

@ -7,6 +7,7 @@ use PhpParser\Node;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Else_;
use PhpParser\Node\Stmt\If_;
use PhpParser\NodeTraverser;
use Rector\Core\Rector\AbstractRector;
use Rector\DeadCode\ConditionEvaluator;
use Rector\DeadCode\ConditionResolver;
@ -60,9 +61,9 @@ CODE_SAMPLE
}
/**
* @param If_ $node
* @return Stmt[]|null
* @return Stmt[]|null|int
*/
public function refactor(Node $node) : ?array
public function refactor(Node $node)
{
if ($node->elseifs !== []) {
return null;
@ -95,14 +96,13 @@ CODE_SAMPLE
return $if->stmts;
}
/**
* @return Stmt[]|null
* @return Stmt[]|int
*/
private function refactorIsNotMatch(If_ $if) : ?array
private function refactorIsNotMatch(If_ $if)
{
// no else → just remove the node
if (!$if->else instanceof Else_) {
$this->removeNode($if);
return null;
return NodeTraverser::REMOVE_NODE;
}
// else is always used
return $if->else->stmts;

View File

@ -11,7 +11,6 @@ use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\Expression;
use PhpParser\NodeTraverser;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Core\Enum\ObjectReference;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
@ -81,51 +80,51 @@ CODE_SAMPLE
if ($this->shouldSkipClass($node)) {
return null;
}
$class = $node;
$hasChanged = \false;
foreach ($node->getMethods() as $classMethod) {
$this->traverseNodesWithCallable($classMethod, function (Node $node) use($class) {
// skip nested anonmyous class
if ($node instanceof Class_) {
return NodeTraverser::STOP_TRAVERSAL;
if ($classMethod->stmts === null) {
continue;
}
foreach ($classMethod->stmts as $key => $stmt) {
if (!$stmt instanceof Expression) {
continue;
}
if ($node instanceof Assign) {
return $this->refactorAssign($node, $class);
if ($stmt->expr instanceof StaticCall && $this->isParentStaticCall($stmt->expr)) {
if ($this->doesCalledMethodExistInParent($stmt->expr, $node)) {
continue;
}
unset($classMethod->stmts[$key]);
$hasChanged = \true;
}
if ($node instanceof Expression) {
$this->refactorExpression($node, $class);
return null;
if ($stmt->expr instanceof Assign) {
$assign = $stmt->expr;
if ($assign->expr instanceof StaticCall && $this->isParentStaticCall($assign->expr)) {
$staticCall = $assign->expr;
// is valid call
if ($this->doesCalledMethodExistInParent($staticCall, $node)) {
continue;
}
$assign->expr = $this->nodeFactory->createNull();
$hasChanged = \true;
}
}
return null;
});
}
}
if ($hasChanged) {
return $node;
}
return null;
}
public function refactorAssign(Assign $assign, Class_ $class) : ?Assign
{
if (!$this->isParentStaticCall($assign->expr)) {
return null;
}
/** @var StaticCall $staticCall */
$staticCall = $assign->expr;
// is valid call
if ($this->doesCalledMethodExistInParent($staticCall, $class)) {
return null;
}
$assign->expr = $this->nodeFactory->createNull();
return $assign;
}
private function isParentStaticCall(Expr $expr) : bool
{
if (!$expr instanceof StaticCall) {
return \false;
}
if (!$expr->class instanceof Name) {
return \false;
}
return $this->isName($expr->class, ObjectReference::PARENT);
}
private function shouldSkipClass(Class_ $class) : bool
{
// skip cases when parent class reflection is not found
if ($class->extends instanceof FullyQualified && !$this->reflectionProvider->hasClass($class->extends->toString())) {
return \true;
}
@ -143,18 +142,4 @@ CODE_SAMPLE
}
return $this->classMethodManipulator->hasParentMethodOrInterfaceMethod($class, $calledMethodName);
}
private function refactorExpression(Expression $expression, Class_ $class) : void
{
if (!$expression->expr instanceof StaticCall) {
return;
}
if (!$this->isParentStaticCall($expression->expr)) {
return;
}
// is valid call
if ($this->doesCalledMethodExistInParent($expression->expr, $class)) {
return;
}
$this->removeNode($expression);
}
}

View File

@ -5,7 +5,6 @@ namespace Rector\Php70\Rector\Switch_;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Stmt\Case_;
use PhpParser\Node\Stmt\Switch_;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
@ -65,37 +64,20 @@ CODE_SAMPLE
}
$defaultCases[$key] = $case;
}
if (\count($defaultCases) < 2) {
$defaultCaseCount = \count($defaultCases);
if ($defaultCaseCount < 2) {
return null;
}
$this->removeExtraDefaultCases($node->cases, $defaultCases);
foreach ($node->cases as $key => $case) {
if ($case->cond instanceof Expr) {
continue;
}
// remove previous default cases
if ($defaultCaseCount > 1) {
unset($node->cases[$key]);
--$defaultCaseCount;
}
}
return $node;
}
/**
* @param Case_[] $cases
* @param Case_[] $defaultCases
*/
private function removeExtraDefaultCases(array $cases, array $defaultCases) : void
{
// keep only last
\array_pop($defaultCases);
foreach ($defaultCases as $key => $defaultCase) {
$this->keepStatementsToParentCase($cases, $defaultCase, $key);
$this->removeNode($defaultCase);
}
}
/**
* @param Case_[] $cases
*/
private function keepStatementsToParentCase(array $cases, Case_ $case, int $key) : void
{
if (!isset($cases[$key - 1])) {
return;
}
$previousCase = $cases[$key - 1];
if ($previousCase->stmts === []) {
$previousCase->stmts = $case->stmts;
$case->stmts = [];
}
}
}

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = '1fa8672242253aae77cd888400c5125d2e1fa9f5';
public const PACKAGE_VERSION = 'a61fbf265d2995453ad8d9f12b6bb00ff1937162';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2023-06-05 10:17:40';
public const RELEASE_DATE = '2023-06-05 13:38:47';
/**
* @var int
*/

View File

@ -59,8 +59,7 @@ A) Direct return null for no change:
B) Remove the Node:
\$this->removeNode(\$node);
return null;
return NodeTraverser::REMOVE_NODE;
CODE_SAMPLE;
/**
* @var \Rector\NodeNameResolver\NodeNameResolver

2
vendor/autoload.php vendored
View File

@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
require_once __DIR__ . '/composer/autoload_real.php';
return ComposerAutoloaderInit235df547e969478b4de18a8fbf52548b::getLoader();
return ComposerAutoloaderInit3a11c776258046af5ada1aa5e6635840::getLoader();

View File

@ -2452,6 +2452,7 @@ return array(
'Rector\\Strict\\Rector\\If_\\BooleanInIfConditionRuleFixerRector' => $baseDir . '/rules/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector.php',
'Rector\\Strict\\Rector\\Ternary\\BooleanInTernaryOperatorRuleFixerRector' => $baseDir . '/rules/Strict/Rector/Ternary/BooleanInTernaryOperatorRuleFixerRector.php',
'Rector\\Strict\\Rector\\Ternary\\DisallowedShortTernaryRuleFixerRector' => $baseDir . '/rules/Strict/Rector/Ternary/DisallowedShortTernaryRuleFixerRector.php',
'Rector\\Symfony\\Annotation\\AnnotationAnalyzer' => $vendorDir . '/rector/rector-symfony/src/Annotation/AnnotationAnalyzer.php',
'Rector\\Symfony\\ApplicationMetadata\\ListenerServiceDefinitionProvider' => $vendorDir . '/rector/rector-symfony/src/ApplicationMetadata/ListenerServiceDefinitionProvider.php',
'Rector\\Symfony\\Bridge\\NodeAnalyzer\\ControllerMethodAnalyzer' => $vendorDir . '/rector/rector-symfony/src/Bridge/NodeAnalyzer/ControllerMethodAnalyzer.php',
'Rector\\Symfony\\Bridge\\Symfony\\ContainerServiceProvider' => $vendorDir . '/rector/rector-symfony/src/Bridge/Symfony/ContainerServiceProvider.php',
@ -2464,6 +2465,7 @@ return array(
'Rector\\Symfony\\DataProvider\\ServiceNameToTypeUniqueProvider' => $vendorDir . '/rector/rector-symfony/src/DataProvider/ServiceNameToTypeUniqueProvider.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',
'Rector\\Symfony\\Enum\\SymfonyClass' => $vendorDir . '/rector/rector-symfony/src/Enum/SymfonyClass.php',
'Rector\\Symfony\\Exception\\InvalidConfigurationException' => $vendorDir . '/rector/rector-symfony/src/Exception/InvalidConfigurationException.php',
'Rector\\Symfony\\Exception\\XmlContainerNotExistsException' => $vendorDir . '/rector/rector-symfony/src/Exception/XmlContainerNotExistsException.php',
'Rector\\Symfony\\FormHelper\\FormTypeStringToTypeProvider' => $vendorDir . '/rector/rector-symfony/src/FormHelper/FormTypeStringToTypeProvider.php',
@ -2586,7 +2588,6 @@ return array(
'Rector\\Symfony\\Rector\\New_\\PropertyPathMapperToDataMapperRector' => $vendorDir . '/rector/rector-symfony/src/Rector/New_/PropertyPathMapperToDataMapperRector.php',
'Rector\\Symfony\\Rector\\New_\\RootNodeTreeBuilderRector' => $vendorDir . '/rector/rector-symfony/src/Rector/New_/RootNodeTreeBuilderRector.php',
'Rector\\Symfony\\Rector\\New_\\StringToArrayArgumentProcessRector' => $vendorDir . '/rector/rector-symfony/src/Rector/New_/StringToArrayArgumentProcessRector.php',
'Rector\\Symfony\\Rector\\Property\\JMSInjectPropertyToConstructorInjectionRector' => $vendorDir . '/rector/rector-symfony/src/Rector/Property/JMSInjectPropertyToConstructorInjectionRector.php',
'Rector\\Symfony\\Rector\\Return_\\SimpleFunctionAndFilterRector' => $vendorDir . '/rector/rector-symfony/src/Rector/Return_/SimpleFunctionAndFilterRector.php',
'Rector\\Symfony\\Rector\\StaticCall\\AddMessageToEqualsResponseCodeRector' => $vendorDir . '/rector/rector-symfony/src/Rector/StaticCall/AddMessageToEqualsResponseCodeRector.php',
'Rector\\Symfony\\Rector\\StaticCall\\BinaryFileResponseCreateToNewInstanceRector' => $vendorDir . '/rector/rector-symfony/src/Rector/StaticCall/BinaryFileResponseCreateToNewInstanceRector.php',
@ -2606,7 +2607,6 @@ return array(
'Rector\\Symfony\\TypeAnalyzer\\ArrayUnionResponseTypeAnalyzer' => $vendorDir . '/rector/rector-symfony/src/TypeAnalyzer/ArrayUnionResponseTypeAnalyzer.php',
'Rector\\Symfony\\TypeAnalyzer\\ContainerAwareAnalyzer' => $vendorDir . '/rector/rector-symfony/src/TypeAnalyzer/ContainerAwareAnalyzer.php',
'Rector\\Symfony\\TypeAnalyzer\\ControllerAnalyzer' => $vendorDir . '/rector/rector-symfony/src/TypeAnalyzer/ControllerAnalyzer.php',
'Rector\\Symfony\\TypeAnalyzer\\JMSDITypeResolver' => $vendorDir . '/rector/rector-symfony/src/TypeAnalyzer/JMSDITypeResolver.php',
'Rector\\Symfony\\TypeDeclaration\\ReturnTypeDeclarationUpdater' => $vendorDir . '/rector/rector-symfony/src/TypeDeclaration/ReturnTypeDeclarationUpdater.php',
'Rector\\Symfony\\ValueObjectFactory\\ServiceMapFactory' => $vendorDir . '/rector/rector-symfony/src/ValueObjectFactory/ServiceMapFactory.php',
'Rector\\Symfony\\ValueObject\\ClassNameAndFilePath' => $vendorDir . '/rector/rector-symfony/src/ValueObject/ClassNameAndFilePath.php',

View File

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit235df547e969478b4de18a8fbf52548b
class ComposerAutoloaderInit3a11c776258046af5ada1aa5e6635840
{
private static $loader;
@ -22,17 +22,17 @@ class ComposerAutoloaderInit235df547e969478b4de18a8fbf52548b
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit235df547e969478b4de18a8fbf52548b', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInit3a11c776258046af5ada1aa5e6635840', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__));
spl_autoload_unregister(array('ComposerAutoloaderInit235df547e969478b4de18a8fbf52548b', 'loadClassLoader'));
spl_autoload_unregister(array('ComposerAutoloaderInit3a11c776258046af5ada1aa5e6635840', 'loadClassLoader'));
require __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit235df547e969478b4de18a8fbf52548b::getInitializer($loader));
call_user_func(\Composer\Autoload\ComposerStaticInit3a11c776258046af5ada1aa5e6635840::getInitializer($loader));
$loader->setClassMapAuthoritative(true);
$loader->register(true);
$filesToLoad = \Composer\Autoload\ComposerStaticInit235df547e969478b4de18a8fbf52548b::$files;
$filesToLoad = \Composer\Autoload\ComposerStaticInit3a11c776258046af5ada1aa5e6635840::$files;
$requireFile = \Closure::bind(static function ($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;

View File

@ -4,7 +4,7 @@
namespace Composer\Autoload;
class ComposerStaticInit235df547e969478b4de18a8fbf52548b
class ComposerStaticInit3a11c776258046af5ada1aa5e6635840
{
public static $files = array (
'ad155f8f1cf0d418fe49e248db8c661b' => __DIR__ . '/..' . '/react/promise/src/functions_include.php',
@ -2694,6 +2694,7 @@ class ComposerStaticInit235df547e969478b4de18a8fbf52548b
'Rector\\Strict\\Rector\\If_\\BooleanInIfConditionRuleFixerRector' => __DIR__ . '/../..' . '/rules/Strict/Rector/If_/BooleanInIfConditionRuleFixerRector.php',
'Rector\\Strict\\Rector\\Ternary\\BooleanInTernaryOperatorRuleFixerRector' => __DIR__ . '/../..' . '/rules/Strict/Rector/Ternary/BooleanInTernaryOperatorRuleFixerRector.php',
'Rector\\Strict\\Rector\\Ternary\\DisallowedShortTernaryRuleFixerRector' => __DIR__ . '/../..' . '/rules/Strict/Rector/Ternary/DisallowedShortTernaryRuleFixerRector.php',
'Rector\\Symfony\\Annotation\\AnnotationAnalyzer' => __DIR__ . '/..' . '/rector/rector-symfony/src/Annotation/AnnotationAnalyzer.php',
'Rector\\Symfony\\ApplicationMetadata\\ListenerServiceDefinitionProvider' => __DIR__ . '/..' . '/rector/rector-symfony/src/ApplicationMetadata/ListenerServiceDefinitionProvider.php',
'Rector\\Symfony\\Bridge\\NodeAnalyzer\\ControllerMethodAnalyzer' => __DIR__ . '/..' . '/rector/rector-symfony/src/Bridge/NodeAnalyzer/ControllerMethodAnalyzer.php',
'Rector\\Symfony\\Bridge\\Symfony\\ContainerServiceProvider' => __DIR__ . '/..' . '/rector/rector-symfony/src/Bridge/Symfony/ContainerServiceProvider.php',
@ -2706,6 +2707,7 @@ class ComposerStaticInit235df547e969478b4de18a8fbf52548b
'Rector\\Symfony\\DataProvider\\ServiceNameToTypeUniqueProvider' => __DIR__ . '/..' . '/rector/rector-symfony/src/DataProvider/ServiceNameToTypeUniqueProvider.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',
'Rector\\Symfony\\Enum\\SymfonyClass' => __DIR__ . '/..' . '/rector/rector-symfony/src/Enum/SymfonyClass.php',
'Rector\\Symfony\\Exception\\InvalidConfigurationException' => __DIR__ . '/..' . '/rector/rector-symfony/src/Exception/InvalidConfigurationException.php',
'Rector\\Symfony\\Exception\\XmlContainerNotExistsException' => __DIR__ . '/..' . '/rector/rector-symfony/src/Exception/XmlContainerNotExistsException.php',
'Rector\\Symfony\\FormHelper\\FormTypeStringToTypeProvider' => __DIR__ . '/..' . '/rector/rector-symfony/src/FormHelper/FormTypeStringToTypeProvider.php',
@ -2828,7 +2830,6 @@ class ComposerStaticInit235df547e969478b4de18a8fbf52548b
'Rector\\Symfony\\Rector\\New_\\PropertyPathMapperToDataMapperRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/New_/PropertyPathMapperToDataMapperRector.php',
'Rector\\Symfony\\Rector\\New_\\RootNodeTreeBuilderRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/New_/RootNodeTreeBuilderRector.php',
'Rector\\Symfony\\Rector\\New_\\StringToArrayArgumentProcessRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/New_/StringToArrayArgumentProcessRector.php',
'Rector\\Symfony\\Rector\\Property\\JMSInjectPropertyToConstructorInjectionRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/Property/JMSInjectPropertyToConstructorInjectionRector.php',
'Rector\\Symfony\\Rector\\Return_\\SimpleFunctionAndFilterRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/Return_/SimpleFunctionAndFilterRector.php',
'Rector\\Symfony\\Rector\\StaticCall\\AddMessageToEqualsResponseCodeRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/StaticCall/AddMessageToEqualsResponseCodeRector.php',
'Rector\\Symfony\\Rector\\StaticCall\\BinaryFileResponseCreateToNewInstanceRector' => __DIR__ . '/..' . '/rector/rector-symfony/src/Rector/StaticCall/BinaryFileResponseCreateToNewInstanceRector.php',
@ -2848,7 +2849,6 @@ class ComposerStaticInit235df547e969478b4de18a8fbf52548b
'Rector\\Symfony\\TypeAnalyzer\\ArrayUnionResponseTypeAnalyzer' => __DIR__ . '/..' . '/rector/rector-symfony/src/TypeAnalyzer/ArrayUnionResponseTypeAnalyzer.php',
'Rector\\Symfony\\TypeAnalyzer\\ContainerAwareAnalyzer' => __DIR__ . '/..' . '/rector/rector-symfony/src/TypeAnalyzer/ContainerAwareAnalyzer.php',
'Rector\\Symfony\\TypeAnalyzer\\ControllerAnalyzer' => __DIR__ . '/..' . '/rector/rector-symfony/src/TypeAnalyzer/ControllerAnalyzer.php',
'Rector\\Symfony\\TypeAnalyzer\\JMSDITypeResolver' => __DIR__ . '/..' . '/rector/rector-symfony/src/TypeAnalyzer/JMSDITypeResolver.php',
'Rector\\Symfony\\TypeDeclaration\\ReturnTypeDeclarationUpdater' => __DIR__ . '/..' . '/rector/rector-symfony/src/TypeDeclaration/ReturnTypeDeclarationUpdater.php',
'Rector\\Symfony\\ValueObjectFactory\\ServiceMapFactory' => __DIR__ . '/..' . '/rector/rector-symfony/src/ValueObjectFactory/ServiceMapFactory.php',
'Rector\\Symfony\\ValueObject\\ClassNameAndFilePath' => __DIR__ . '/..' . '/rector/rector-symfony/src/ValueObject/ClassNameAndFilePath.php',
@ -3051,9 +3051,9 @@ class ComposerStaticInit235df547e969478b4de18a8fbf52548b
public static function getInitializer(ClassLoader $loader)
{
return \Closure::bind(function () use ($loader) {
$loader->prefixLengthsPsr4 = ComposerStaticInit235df547e969478b4de18a8fbf52548b::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit235df547e969478b4de18a8fbf52548b::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit235df547e969478b4de18a8fbf52548b::$classMap;
$loader->prefixLengthsPsr4 = ComposerStaticInit3a11c776258046af5ada1aa5e6635840::$prefixLengthsPsr4;
$loader->prefixDirsPsr4 = ComposerStaticInit3a11c776258046af5ada1aa5e6635840::$prefixDirsPsr4;
$loader->classMap = ComposerStaticInit3a11c776258046af5ada1aa5e6635840::$classMap;
}, null, ClassLoader::class);
}

View File

@ -2056,12 +2056,12 @@
"source": {
"type": "git",
"url": "https:\/\/github.com\/rectorphp\/rector-symfony.git",
"reference": "1ad0ae7d9cc161537963a79af2add5d81d3436f2"
"reference": "36d3cb5f9cd561d8fdb39e560c78f5faa87385b5"
},
"dist": {
"type": "zip",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-symfony\/zipball\/1ad0ae7d9cc161537963a79af2add5d81d3436f2",
"reference": "1ad0ae7d9cc161537963a79af2add5d81d3436f2",
"url": "https:\/\/api.github.com\/repos\/rectorphp\/rector-symfony\/zipball\/36d3cb5f9cd561d8fdb39e560c78f5faa87385b5",
"reference": "36d3cb5f9cd561d8fdb39e560c78f5faa87385b5",
"shasum": ""
},
"require": {
@ -2091,7 +2091,7 @@
"tomasvotruba\/type-coverage": "^0.2",
"tomasvotruba\/unused-public": "^0.1"
},
"time": "2023-06-05T09:03:48+00:00",
"time": "2023-06-05T12:52:41+00:00",
"default-branch": true,
"type": "rector-extension",
"extra": {
@ -2115,7 +2115,7 @@
"description": "Rector upgrades rules for Symfony Framework",
"support": {
"issues": "https:\/\/github.com\/rectorphp\/rector-symfony\/issues",
"source": "https:\/\/github.com\/rectorphp\/rector-symfony\/tree\/main"
"source": "https:\/\/github.com\/rectorphp\/rector-symfony\/tree\/0.15.0"
},
"install-path": "..\/rector\/rector-symfony"
},

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/vendor/rector/rector-doctrine', 'relative_install_path' => '../../rector-doctrine', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 08503e7'), 'rector/rector-downgrade-php' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-downgrade-php', 'relative_install_path' => '../../rector-downgrade-php', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 7e64cfd'), 'rector/rector-phpunit' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-phpunit', 'relative_install_path' => '../../rector-phpunit', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 71fabfb'), 'rector/rector-symfony' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-symfony', 'relative_install_path' => '../../rector-symfony', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 1ad0ae7'));
public const EXTENSIONS = array('rector/rector-doctrine' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-doctrine', 'relative_install_path' => '../../rector-doctrine', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 08503e7'), 'rector/rector-downgrade-php' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-downgrade-php', 'relative_install_path' => '../../rector-downgrade-php', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 7e64cfd'), 'rector/rector-phpunit' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-phpunit', 'relative_install_path' => '../../rector-phpunit', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 71fabfb'), 'rector/rector-symfony' => array('install_path' => '/home/runner/work/rector-src/rector-src/vendor/rector/rector-symfony', 'relative_install_path' => '../../rector-symfony', 'extra' => array('includes' => array(0 => 'config/config.php')), 'version' => 'dev-main 36d3cb5'));
private function __construct()
{
}

View File

@ -1,10 +0,0 @@
<?php
declare (strict_types=1);
namespace RectorPrefix202306;
use Rector\Config\RectorConfig;
use Rector\Symfony\Rector\Property\JMSInjectPropertyToConstructorInjectionRector;
return static function (RectorConfig $rectorConfig) : void {
$rectorConfig->rule(JMSInjectPropertyToConstructorInjectionRector::class);
};

View File

@ -0,0 +1,41 @@
<?php
declare (strict_types=1);
namespace Rector\Symfony\Annotation;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\Symfony\Enum\SymfonyAnnotation;
final class AnnotationAnalyzer
{
/**
* @readonly
* @var \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory
*/
private $phpDocInfoFactory;
public function __construct(PhpDocInfoFactory $phpDocInfoFactory)
{
$this->phpDocInfoFactory = $phpDocInfoFactory;
}
public function hasClassMethodWithTemplateAnnotation(Class_ $class) : bool
{
foreach ($class->getMethods() as $classMethod) {
$templateDoctrineAnnotationTagValueNode = $this->getDoctrineAnnotationTagValueNode($classMethod, SymfonyAnnotation::TEMPLATE);
if ($templateDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return \true;
}
}
return \false;
}
public function getDoctrineAnnotationTagValueNode(ClassMethod $classMethod, string $annotationClass) : ?DoctrineAnnotationTagValueNode
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod);
if (!$phpDocInfo instanceof PhpDocInfo) {
return null;
}
return $phpDocInfo->getByAnnotationClass($annotationClass);
}
}

View File

@ -17,6 +17,10 @@ final class SymfonyAnnotation
* @var string
*/
public const MAP_ENTITY = 'Symfony\\Bridge\\Doctrine\\Attribute\\MapEntity';
/**
* @var string
*/
public const TEMPLATE = 'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Template';
/**
* @var string
*/

View File

@ -0,0 +1,12 @@
<?php
declare (strict_types=1);
namespace Rector\Symfony\Enum;
final class SymfonyClass
{
/**
* @var string
*/
public const RESPONSE = 'Symfony\\Component\\HttpFoundation\\Response';
}

View File

@ -11,7 +11,6 @@ use PhpParser\Node\Expr\Variable;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\Node\NodeFactory;
use Rector\NodeRemoval\NodeRemover;
use Rector\Symfony\NodeAnalyzer\FormType\CreateFormTypeOptionsArgMover;
use Rector\Symfony\NodeAnalyzer\FormType\FormTypeClassResolver;
final class FormInstanceToFormClassConstFetchConverter
@ -36,18 +35,12 @@ final class FormInstanceToFormClassConstFetchConverter
* @var \Rector\Core\PhpParser\Node\BetterNodeFinder
*/
private $betterNodeFinder;
/**
* @readonly
* @var \Rector\NodeRemoval\NodeRemover
*/
private $nodeRemover;
public function __construct(CreateFormTypeOptionsArgMover $createFormTypeOptionsArgMover, NodeFactory $nodeFactory, FormTypeClassResolver $formTypeClassResolver, BetterNodeFinder $betterNodeFinder, NodeRemover $nodeRemover)
public function __construct(CreateFormTypeOptionsArgMover $createFormTypeOptionsArgMover, NodeFactory $nodeFactory, FormTypeClassResolver $formTypeClassResolver, BetterNodeFinder $betterNodeFinder)
{
$this->createFormTypeOptionsArgMover = $createFormTypeOptionsArgMover;
$this->nodeFactory = $nodeFactory;
$this->formTypeClassResolver = $formTypeClassResolver;
$this->betterNodeFinder = $betterNodeFinder;
$this->nodeRemover = $nodeRemover;
}
public function processNewInstance(MethodCall $methodCall, int $position, int $optionsPosition) : ?MethodCall
{
@ -67,11 +60,6 @@ final class FormInstanceToFormClassConstFetchConverter
throw new ShouldNotHappenException();
}
}
// remove previous assign
$previousAssign = $this->betterNodeFinder->findPreviousAssignToExpr($argValue);
if ($previousAssign instanceof Assign) {
$this->nodeRemover->removeNode($previousAssign);
}
$classConstFetch = $this->nodeFactory->createClassConstReference($formClassName);
$currentArg = $methodCall->getArgs()[$position];
$currentArg->value = $classConstFetch;

View File

@ -4,21 +4,14 @@ declare (strict_types=1);
namespace Rector\Symfony\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Arg;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\Instanceof_;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\If_;
use PhpParser\Node\Stmt\Return_;
use PhpParser\NodeTraverser;
use PHPStan\Type\ArrayType;
@ -26,17 +19,18 @@ use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTagRemover;
use Rector\CodeQuality\NodeTypeGroup;
use Rector\Core\Contract\PhpParser\Node\StmtsAwareInterface;
use Rector\Core\Rector\AbstractRector;
use Rector\Symfony\Annotation\AnnotationAnalyzer;
use Rector\Symfony\Enum\SymfonyAnnotation;
use Rector\Symfony\Enum\SymfonyClass;
use Rector\Symfony\NodeFactory\ThisRenderFactory;
use Rector\Symfony\NodeFinder\EmptyReturnNodeFinder;
use Rector\Symfony\TypeAnalyzer\ArrayUnionResponseTypeAnalyzer;
use Rector\Symfony\TypeDeclaration\ReturnTypeDeclarationUpdater;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
use RectorPrefix202306\Webmozart\Assert\Assert;
/**
* @changelog https://github.com/symfony/symfony-docs/pull/12387#discussion_r329551967
* @changelog https://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/view.html
@ -46,14 +40,6 @@ use RectorPrefix202306\Webmozart\Assert\Assert;
*/
final class TemplateAnnotationToThisRenderRector extends AbstractRector
{
/**
* @var class-string
*/
private const RESPONSE_CLASS = 'Symfony\\Component\\HttpFoundation\\Response';
/**
* @var string
*/
private const TEMPLATE_ANNOTATION_CLASS = 'Sensio\\Bundle\\FrameworkExtraBundle\\Configuration\\Template';
/**
* @readonly
* @var \Rector\Symfony\TypeAnalyzer\ArrayUnionResponseTypeAnalyzer
@ -79,28 +65,44 @@ final class TemplateAnnotationToThisRenderRector extends AbstractRector
* @var \Rector\Symfony\NodeFinder\EmptyReturnNodeFinder
*/
private $emptyReturnNodeFinder;
public function __construct(ArrayUnionResponseTypeAnalyzer $arrayUnionResponseTypeAnalyzer, ReturnTypeDeclarationUpdater $returnTypeDeclarationUpdater, ThisRenderFactory $thisRenderFactory, PhpDocTagRemover $phpDocTagRemover, EmptyReturnNodeFinder $emptyReturnNodeFinder)
/**
* @readonly
* @var \Rector\Symfony\Annotation\AnnotationAnalyzer
*/
private $annotationAnalyzer;
public function __construct(ArrayUnionResponseTypeAnalyzer $arrayUnionResponseTypeAnalyzer, ReturnTypeDeclarationUpdater $returnTypeDeclarationUpdater, ThisRenderFactory $thisRenderFactory, PhpDocTagRemover $phpDocTagRemover, EmptyReturnNodeFinder $emptyReturnNodeFinder, AnnotationAnalyzer $annotationAnalyzer)
{
$this->arrayUnionResponseTypeAnalyzer = $arrayUnionResponseTypeAnalyzer;
$this->returnTypeDeclarationUpdater = $returnTypeDeclarationUpdater;
$this->thisRenderFactory = $thisRenderFactory;
$this->phpDocTagRemover = $phpDocTagRemover;
$this->emptyReturnNodeFinder = $emptyReturnNodeFinder;
$this->annotationAnalyzer = $annotationAnalyzer;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('Turns `@Template` annotation to explicit method call in Controller of FrameworkExtraBundle in Symfony', [new CodeSample(<<<'CODE_SAMPLE'
/**
* @Template()
*/
public function indexAction()
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
final class SomeController
{
/**
* @Template()
*/
public function indexAction()
{
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
public function indexAction()
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
final class SomeController
{
return $this->render('index.html.twig');
public function indexAction()
{
return $this->render('index.html.twig');
}
}
CODE_SAMPLE
)]);
@ -127,35 +129,24 @@ CODE_SAMPLE
if ($class->extends instanceof Name) {
return null;
}
if (!$this->hasClassMethodWithTemplateAnnotation($class)) {
if (!$this->annotationAnalyzer->hasClassMethodWithTemplateAnnotation($class)) {
return null;
}
$class->extends = new FullyQualified('Symfony\\Bundle\\FrameworkBundle\\Controller\\AbstractController');
return $class;
}
private function replaceTemplateAnnotation(ClassMethod $classMethod) : ?Node
private function replaceTemplateAnnotation(ClassMethod $classMethod) : ?ClassMethod
{
if (!$classMethod->isPublic()) {
return null;
}
$doctrineAnnotationTagValueNode = $this->getDoctrineAnnotationTagValueNode($classMethod, self::TEMPLATE_ANNOTATION_CLASS);
$doctrineAnnotationTagValueNode = $this->annotationAnalyzer->getDoctrineAnnotationTagValueNode($classMethod, SymfonyAnnotation::TEMPLATE);
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return null;
}
$this->refactorClassMethod($classMethod, $doctrineAnnotationTagValueNode);
return $classMethod;
return $this->refactorClassMethod($classMethod, $doctrineAnnotationTagValueNode);
}
private function hasClassMethodWithTemplateAnnotation(Class_ $class) : bool
{
foreach ($class->getMethods() as $classMethod) {
$templateDoctrineAnnotationTagValueNode = $this->getDoctrineAnnotationTagValueNode($classMethod, self::TEMPLATE_ANNOTATION_CLASS);
if ($templateDoctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return \true;
}
}
return \false;
}
private function refactorClassMethod(ClassMethod $classMethod, DoctrineAnnotationTagValueNode $templateDoctrineAnnotationTagValueNode) : void
private function refactorClassMethod(ClassMethod $classMethod, DoctrineAnnotationTagValueNode $templateDoctrineAnnotationTagValueNode) : ?ClassMethod
{
$hasThisRenderOrReturnsResponse = $this->hasLastReturnResponse($classMethod);
$this->traverseNodesWithCallable($classMethod, function (Node $node) use($templateDoctrineAnnotationTagValueNode, $hasThisRenderOrReturnsResponse, $classMethod) {
@ -163,23 +154,18 @@ CODE_SAMPLE
if ($node instanceof Closure || $node instanceof Function_) {
return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
}
if (!$node instanceof Stmt) {
return null;
}
foreach (NodeTypeGroup::STMTS_AWARE as $stmtsAwareType) {
if (!$node instanceof $stmtsAwareType) {
continue;
}
$this->refactorStmtsAwareNode($node, $templateDoctrineAnnotationTagValueNode, $hasThisRenderOrReturnsResponse, $classMethod);
if (!$node instanceof StmtsAwareInterface) {
return null;
}
$this->refactorStmtsAwareNode($node, $templateDoctrineAnnotationTagValueNode, $hasThisRenderOrReturnsResponse, $classMethod);
return null;
});
if (!$this->emptyReturnNodeFinder->hasNoOrEmptyReturns($classMethod)) {
return;
return null;
}
$thisRenderMethodCall = $this->thisRenderFactory->create(null, $templateDoctrineAnnotationTagValueNode, $classMethod);
$this->refactorNoReturn($classMethod, $thisRenderMethodCall, $templateDoctrineAnnotationTagValueNode);
return $classMethod;
}
private function hasLastReturnResponse(ClassMethod $classMethod) : bool
{
@ -190,7 +176,7 @@ CODE_SAMPLE
if (!$node->expr instanceof Expr) {
return \false;
}
$responseObjectType = new ObjectType(self::RESPONSE_CLASS);
$responseObjectType = new ObjectType(SymfonyClass::RESPONSE);
$returnType = $this->getType($node->expr);
return $responseObjectType->isSuperTypeOf($returnType)->yes();
}
@ -204,18 +190,10 @@ CODE_SAMPLE
$thisRenderMethodCall = $this->thisRenderFactory->create($return, $templateDoctrineAnnotationTagValueNode, $classMethod);
$this->refactorReturnWithValue($return, $hasThisRenderOrReturnsResponse, $thisRenderMethodCall, $classMethod, $templateDoctrineAnnotationTagValueNode);
}
private function getDoctrineAnnotationTagValueNode(ClassMethod $classMethod, string $class) : ?DoctrineAnnotationTagValueNode
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($classMethod);
if (!$phpDocInfo instanceof PhpDocInfo) {
return null;
}
return $phpDocInfo->getByAnnotationClass($class);
}
private function refactorNoReturn(ClassMethod $classMethod, MethodCall $thisRenderMethodCall, DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode) : void
{
$classMethod->stmts[] = new Return_($thisRenderMethodCall);
$this->returnTypeDeclarationUpdater->updateClassMethod($classMethod, self::RESPONSE_CLASS);
$this->returnTypeDeclarationUpdater->updateClassMethod($classMethod, SymfonyClass::RESPONSE);
$this->removeDoctrineAnnotationTagValueNode($classMethod, $doctrineAnnotationTagValueNode);
}
private function refactorReturnWithValue(Return_ $return, bool $hasThisRenderOrReturnsResponse, MethodCall $thisRenderMethodCall, ClassMethod $classMethod, DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode) : void
@ -233,37 +211,26 @@ CODE_SAMPLE
// nothing we can do
return;
}
$isArrayOrResponseType = $this->arrayUnionResponseTypeAnalyzer->isArrayUnionResponseType($returnStaticType, self::RESPONSE_CLASS);
$isArrayOrResponseType = $this->arrayUnionResponseTypeAnalyzer->isArrayUnionResponseType($returnStaticType, SymfonyClass::RESPONSE);
// skip as the original class method has to change first
if ($isArrayOrResponseType) {
$this->processIsArrayOrResponseType($classMethod, $return, $lastReturnExpr, $thisRenderMethodCall);
return;
}
// already response
$this->removeDoctrineAnnotationTagValueNode($classMethod, $doctrineAnnotationTagValueNode);
$this->returnTypeDeclarationUpdater->updateClassMethod($classMethod, self::RESPONSE_CLASS);
}
private function processIsArrayOrResponseType(ClassMethod $classMethod, Return_ $return, Expr $returnExpr, MethodCall $thisRenderMethodCall) : void
{
$this->removeNode($return);
// create instance of Response → return response, or return $this->render
$responseVariable = new Variable('responseOrData');
$assign = new Assign($responseVariable, $returnExpr);
$assignExpression = new Expression($assign);
$if = new If_(new Instanceof_($responseVariable, new FullyQualified(self::RESPONSE_CLASS)));
$if->stmts[] = new Return_($responseVariable);
$thisRenderMethodCall->args[1] = new Arg($responseVariable);
$returnThisRender = new Return_($thisRenderMethodCall);
$classMethodStmts = (array) $classMethod->stmts;
$classMethod->stmts = \array_merge($classMethodStmts, [$assignExpression, $if, $returnThisRender]);
$this->returnTypeDeclarationUpdater->updateClassMethod($classMethod, SymfonyClass::RESPONSE);
}
private function removeDoctrineAnnotationTagValueNode(ClassMethod $classMethod, DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode) : void
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $doctrineAnnotationTagValueNode);
}
private function refactorStmtsAwareNode(Stmt $stmtsAwareStmt, DoctrineAnnotationTagValueNode $templateDoctrineAnnotationTagValueNode, bool $hasThisRenderOrReturnsResponse, ClassMethod $classMethod) : void
private function refactorStmtsAwareNode(StmtsAwareInterface $stmtsAware, DoctrineAnnotationTagValueNode $templateDoctrineAnnotationTagValueNode, bool $hasThisRenderOrReturnsResponse, ClassMethod $classMethod) : void
{
Assert::propertyExists($stmtsAwareStmt, 'stmts');
foreach ((array) $stmtsAwareStmt->stmts as $stmt) {
if ($stmtsAware->stmts === null) {
return;
}
foreach ($stmtsAware->stmts as $stmt) {
if (!$stmt instanceof Return_) {
continue;
}

View File

@ -8,6 +8,7 @@ use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\MethodName;
@ -125,11 +126,14 @@ CODE_SAMPLE
$assignExpressions = $this->buildFormOptionAssignsFactory->createDimFetchAssignsFromParamNames($paramNames);
$buildFormClassMethod->stmts = \array_merge($assignExpressions, (array) $buildFormClassMethod->stmts);
// 2. remove properties
foreach ($node->getProperties() as $property) {
if (!$this->isNames($property, $paramNames)) {
foreach ($node->stmts as $key => $stmt) {
if (!$stmt instanceof Property) {
continue;
}
$this->removeNode($property);
if (!$this->isNames($stmt, $paramNames)) {
continue;
}
unset($node->stmts[$key]);
}
// 3. cleanup ctor
$this->constructorDependencyRemover->removeParamsByName($constructorClassMethod, $paramNames);

View File

@ -12,7 +12,6 @@ use Rector\Core\Rector\AbstractRector;
use Rector\Symfony\NodeAnalyzer\ClassAnalyzer;
use Rector\Symfony\NodeFactory\GetSubscribedEventsClassMethodFactory;
use Rector\Symfony\NodeFactory\OnSuccessLogoutClassMethodFactory;
use Rector\Symfony\NodeManipulator\ClassManipulator;
use Rector\Symfony\ValueObject\EventReferenceToMethodNameWithPriority;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -43,17 +42,11 @@ final class LogoutSuccessHandlerToLogoutEventSubscriberRector extends AbstractRe
* @var \Rector\Symfony\NodeAnalyzer\ClassAnalyzer
*/
private $classAnalyzer;
/**
* @readonly
* @var \Rector\Symfony\NodeManipulator\ClassManipulator
*/
private $classManipulator;
public function __construct(OnSuccessLogoutClassMethodFactory $onSuccessLogoutClassMethodFactory, GetSubscribedEventsClassMethodFactory $getSubscribedEventsClassMethodFactory, ClassAnalyzer $classAnalyzer, ClassManipulator $classManipulator)
public function __construct(OnSuccessLogoutClassMethodFactory $onSuccessLogoutClassMethodFactory, GetSubscribedEventsClassMethodFactory $getSubscribedEventsClassMethodFactory, ClassAnalyzer $classAnalyzer)
{
$this->onSuccessLogoutClassMethodFactory = $onSuccessLogoutClassMethodFactory;
$this->getSubscribedEventsClassMethodFactory = $getSubscribedEventsClassMethodFactory;
$this->classAnalyzer = $classAnalyzer;
$this->classManipulator = $classManipulator;
$this->successHandlerObjectType = new ObjectType('Symfony\\Component\\Security\\Http\\Logout\\LogoutSuccessHandlerInterface');
}
public function getRuleDefinition() : RuleDefinition
@ -135,10 +128,20 @@ CODE_SAMPLE
if (!$this->classAnalyzer->hasImplements($node, 'Symfony\\Component\\Security\\Http\\Logout\\LogoutSuccessHandlerInterface')) {
return null;
}
$this->classManipulator->removeImplements($node, [$this->successHandlerObjectType->getClassName()]);
$this->refactorImplements($node);
$node->implements[] = new FullyQualified('Symfony\\Component\\EventDispatcher\\EventSubscriberInterface');
// 2. refactor logout() class method to onLogout()
$onLogoutSuccessClassMethod = $node->getMethod('onLogoutSuccess');
$onLogoutSuccessClassMethod = null;
foreach ($node->stmts as $key => $stmt) {
if (!$stmt instanceof ClassMethod) {
continue;
}
if (!$this->isName($stmt, 'onLogoutSuccess')) {
continue;
}
$onLogoutSuccessClassMethod = $stmt;
unset($node->stmts[$key]);
}
if (!$onLogoutSuccessClassMethod instanceof ClassMethod) {
return null;
}
@ -148,7 +151,15 @@ CODE_SAMPLE
$eventReferencesToMethodNames = [new EventReferenceToMethodNameWithPriority($classConstFetch, 'onLogout', 64)];
$getSubscribedEventsClassMethod = $this->getSubscribedEventsClassMethodFactory->create($eventReferencesToMethodNames);
$node->stmts[] = $getSubscribedEventsClassMethod;
$this->removeNode($onLogoutSuccessClassMethod);
return $node;
}
private function refactorImplements(Class_ $class) : void
{
foreach ($class->implements as $key => $implement) {
if (!$this->isName($implement, $this->successHandlerObjectType->getClassName())) {
continue;
}
unset($class->implements[$key]);
}
}
}

View File

@ -5,17 +5,17 @@ namespace Rector\Symfony\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\VariadicPlaceholder;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\Php\PhpMethodReflection;
use PHPStan\Type\ObjectType;
use Rector\Core\Rector\AbstractScopeAwareRector;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\Core\ValueObject\PhpVersion;
use Rector\NodeCollector\NodeAnalyzer\ArrayCallableMethodMatcher;
use Rector\NodeCollector\ValueObject\ArrayCallable;
use Rector\Php72\NodeFactory\AnonymousFunctionFactory;
use Rector\VersionBonding\Contract\MinPhpVersionInterface;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -23,27 +23,15 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*
* @see PHP 8.1 way to handle functions/filters https://github.com/symfony/symfony/blob/e0ad2eead3513a558c09d8aa3ae9e867fb10b419/src/Symfony/Bridge/Twig/Extension/CodeExtension.php#L41-L52
*/
final class MagicClosureTwigExtensionToNativeMethodsRector extends AbstractScopeAwareRector
final class MagicClosureTwigExtensionToNativeMethodsRector extends AbstractScopeAwareRector implements MinPhpVersionInterface
{
/**
* @readonly
* @var \Rector\Php72\NodeFactory\AnonymousFunctionFactory
*/
private $anonymousFunctionFactory;
/**
* @readonly
* @var \Rector\Core\Reflection\ReflectionResolver
*/
private $reflectionResolver;
/**
* @readonly
* @var \Rector\NodeCollector\NodeAnalyzer\ArrayCallableMethodMatcher
*/
private $arrayCallableMethodMatcher;
public function __construct(AnonymousFunctionFactory $anonymousFunctionFactory, ReflectionResolver $reflectionResolver, ArrayCallableMethodMatcher $arrayCallableMethodMatcher)
public function __construct(ArrayCallableMethodMatcher $arrayCallableMethodMatcher)
{
$this->anonymousFunctionFactory = $anonymousFunctionFactory;
$this->reflectionResolver = $reflectionResolver;
$this->arrayCallableMethodMatcher = $arrayCallableMethodMatcher;
}
public function getRuleDefinition() : RuleDefinition
@ -61,7 +49,6 @@ final class TerminologyExtension extends AbstractExtension
];
}
private function resolve($value)
{
return $value + 100;
@ -77,11 +64,14 @@ final class TerminologyExtension extends AbstractExtension
public function getFunctions(): array
{
return [
new TwigFunction('resolve', function ($values) {
return $value + 100;
}),
new TwigFunction('resolve', $this->resolve(...)),
];
}
private function resolve($value)
{
return $value + 100;
}
}
CODE_SAMPLE
)]);
@ -104,22 +94,26 @@ CODE_SAMPLE
$hasFunctionsChanged = \false;
$getFunctionsClassMethod = $node->getMethod('getFunctions');
if ($getFunctionsClassMethod instanceof ClassMethod) {
$hasFunctionsChanged = $this->refactorClassMethod($node, $getFunctionsClassMethod, $scope);
$hasFunctionsChanged = $this->refactorClassMethod($getFunctionsClassMethod, $scope);
}
$hasFiltersChanged = \false;
$getFiltersClassMethod = $node->getMethod('getFilters');
if ($getFiltersClassMethod instanceof ClassMethod) {
$hasFiltersChanged = $this->refactorClassMethod($node, $getFiltersClassMethod, $scope);
$hasFiltersChanged = $this->refactorClassMethod($getFiltersClassMethod, $scope);
}
if ($hasFiltersChanged || $hasFunctionsChanged) {
return $node;
}
return null;
}
private function refactorClassMethod(Class_ $class, ClassMethod $classMethod, Scope $scope) : bool
public function provideMinPhpVersion() : int
{
return PhpVersion::PHP_81;
}
private function refactorClassMethod(ClassMethod $classMethod, Scope $scope) : bool
{
$hasChanged = \false;
$this->traverseNodesWithCallable($classMethod, function (Node $node) use(&$hasChanged, $class, $scope) : ?Node {
$this->traverseNodesWithCallable($classMethod, function (Node $node) use(&$hasChanged, $scope) : ?Node {
if (!$node instanceof Array_) {
return null;
}
@ -127,26 +121,8 @@ CODE_SAMPLE
if (!$arrayCallable instanceof ArrayCallable) {
return null;
}
$phpMethodReflection = $this->reflectionResolver->resolveMethodReflection($arrayCallable->getClass(), $arrayCallable->getMethod(), null);
if (!$phpMethodReflection instanceof PhpMethodReflection) {
return null;
}
$closure = $this->anonymousFunctionFactory->createFromPhpMethodReflection($phpMethodReflection, $arrayCallable->getCallerExpr());
if (!$closure instanceof Closure) {
return null;
}
// make method private, if local one
$localClassMethod = $class->getMethod($arrayCallable->getMethod());
if ($localClassMethod instanceof ClassMethod) {
$stmtsCount = \count((array) $localClassMethod->stmts);
if ($stmtsCount === 1) {
// inline and remove method
$closure->stmts = $localClassMethod->stmts;
$this->removeNode($localClassMethod);
}
}
$hasChanged = \true;
return $closure;
return new MethodCall($arrayCallable->getCallerExpr(), $arrayCallable->getMethod(), [new VariadicPlaceholder()]);
});
return $hasChanged;
}

View File

@ -5,20 +5,13 @@ namespace Rector\Symfony\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\ClassConstFetch;
use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Scalar\String_;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\ObjectType;
use PHPStan\Type\StringType;
use Rector\Core\NodeAnalyzer\ParamAnalyzer;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\MethodName;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
@ -28,15 +21,6 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
*/
final class MakeCommandLazyRector extends AbstractRector
{
/**
* @readonly
* @var \Rector\Core\NodeAnalyzer\ParamAnalyzer
*/
private $paramAnalyzer;
public function __construct(ParamAnalyzer $paramAnalyzer)
{
$this->paramAnalyzer = $paramAnalyzer;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('Make Symfony commands lazy', [new CodeSample(<<<'CODE_SAMPLE'
@ -82,121 +66,46 @@ CODE_SAMPLE
if ($defaultNameProperty instanceof Property) {
return null;
}
$commandName = $this->resolveCommandName($node);
if (!$commandName instanceof Node) {
$commandNameExpr = $this->resolveCommandNameFromSetName($node);
if (!$commandNameExpr instanceof Expr) {
return null;
}
if (!$commandName instanceof String_ && !$commandName instanceof ClassConstFetch) {
$commandNameType = $this->getType($commandNameExpr);
if (!$commandNameType->isConstantScalarValue()->yes()) {
return null;
}
$this->removeConstructorIfHasOnlySetNameMethodCall($node);
$defaultNameProperty = $this->createStaticProtectedPropertyWithDefault('defaultName', $commandName);
$defaultNameProperty = $this->createStaticProtectedPropertyWithDefault('defaultName', $commandNameExpr);
$node->stmts = \array_merge([$defaultNameProperty], $node->stmts);
return $node;
}
private function resolveCommandName(Class_ $class) : ?Node
private function resolveCommandNameFromSetName(Class_ $class) : ?Expr
{
$node = $this->resolveCommandNameFromConstructor($class);
if (!$node instanceof Node) {
return $this->resolveCommandNameFromSetName($class);
}
return $node;
}
private function resolveCommandNameFromConstructor(Class_ $class) : ?Node
{
$commandName = null;
$this->traverseNodesWithCallable($class->stmts, function (Node $node) use(&$commandName) {
if (!$node instanceof StaticCall) {
return null;
}
if (!$this->isObjectType($node->class, new ObjectType('Symfony\\Component\\Console\\Command\\Command'))) {
return null;
}
$commandName = $this->matchCommandNameNodeInConstruct($node);
if (!$commandName instanceof Expr) {
return null;
}
// only valid static property values for name
if (!$commandName instanceof String_ && !$commandName instanceof ConstFetch) {
return null;
}
// remove if parent name is not string
\array_shift($node->args);
});
return $commandName;
}
private function resolveCommandNameFromSetName(Class_ $class) : ?Node
{
$commandName = null;
$this->traverseNodesWithCallable($class->stmts, function (Node $node) use(&$commandName) {
if (!$node instanceof MethodCall) {
return null;
}
if (!$this->isName($node->name, 'setName')) {
return null;
}
if (!$this->isObjectType($node->var, new ObjectType('Symfony\\Component\\Console\\Command\\Command'))) {
return null;
}
$commandName = $node->getArgs()[0]->value;
$commandNameStaticType = $this->getType($commandName);
if (!$commandNameStaticType instanceof StringType) {
return null;
}
// is chain call? → remove by variable nulling
if ($node->var instanceof MethodCall) {
return $node->var;
}
$this->removeNode($node);
});
return $commandName;
}
private function removeConstructorIfHasOnlySetNameMethodCall(Class_ $class) : void
{
$constructClassMethod = $class->getMethod(MethodName::CONSTRUCT);
if (!$constructClassMethod instanceof ClassMethod) {
return;
}
$stmts = (array) $constructClassMethod->stmts;
if (\count($stmts) !== 1) {
return;
}
$params = $constructClassMethod->getParams();
if ($this->paramAnalyzer->hasPropertyPromotion($params)) {
return;
}
$onlyNode = $stmts[0];
if ($onlyNode instanceof Expression) {
$onlyNode = $onlyNode->expr;
}
if (!$onlyNode instanceof Expr) {
return;
}
if (!$onlyNode instanceof StaticCall) {
return;
}
if (!$this->isName($onlyNode->name, MethodName::CONSTRUCT)) {
return;
}
if ($onlyNode->args !== []) {
return;
}
$this->removeNode($constructClassMethod);
}
private function matchCommandNameNodeInConstruct(StaticCall $staticCall) : ?Expr
{
if (!$this->isName($staticCall->name, MethodName::CONSTRUCT)) {
$configureClassMethod = $class->getMethod('configure');
if (!$configureClassMethod instanceof ClassMethod) {
return null;
}
if (\count($staticCall->args) < 1) {
if ($configureClassMethod->stmts === null) {
return null;
}
$firstArg = $staticCall->getArgs()[0];
$staticType = $this->getType($firstArg->value);
if (!$staticType instanceof StringType) {
return null;
foreach ($configureClassMethod->stmts as $key => $stmt) {
if (!$stmt instanceof Expression) {
continue;
}
if (!$stmt->expr instanceof MethodCall) {
continue;
}
$methodCall = $stmt->expr;
if (!$this->isName($methodCall->name, 'setName')) {
continue;
}
if (!$this->isObjectType($methodCall->var, new ObjectType('Symfony\\Component\\Console\\Command\\Command'))) {
continue;
}
$commandNameEpxr = $methodCall->getArgs()[0]->value;
unset($configureClassMethod->stmts[$key]);
return $commandNameEpxr;
}
return $firstArg->value;
return null;
}
private function createStaticProtectedPropertyWithDefault(string $name, Node $node) : Property
{

View File

@ -18,6 +18,7 @@ use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ObjectType;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\Symfony\NodeAnalyzer\SymfonyPhpClosureDetector;
use Rector\Symfony\ValueObject\ClassNameAndFilePath;
use RectorPrefix202306\Symfony\Component\Filesystem\Filesystem;
@ -100,7 +101,8 @@ CODE_SAMPLE
if (!$this->symfonyPhpClosureDetector->detect($node)) {
return null;
}
$bareServicesSetMethodCalls = $this->collectServiceSetMethodCalls($node);
/** @var array<Expression<MethodCall>> $bareServicesSetMethodCalls */
$bareServicesSetMethodCalls = $this->collectServiceSetMethodCallExpressions($node);
if ($bareServicesSetMethodCalls === []) {
return null;
}
@ -112,12 +114,13 @@ CODE_SAMPLE
$firstClassNameAndFilePath = $classNamesAndFilesPaths[0];
$classFilePath = $firstClassNameAndFilePath->getFilePath();
$directoryConcat = $this->createAbsolutePathConcat($classFilePath);
$args = [new Arg(new String_($sharedNamespace)), new Arg($directoryConcat)];
$loadMethodCall = new MethodCall(new Variable('services'), 'load', $args);
$loadMethodCall = $this->createServicesLoadMethodCall($sharedNamespace, $directoryConcat);
$node->stmts[] = new Expression($loadMethodCall);
// remove all method calls
foreach ($bareServicesSetMethodCalls as $bareServiceSetMethodCall) {
$this->removeNode($bareServiceSetMethodCall);
/** @var Expression $bareServiceSetMethodCall */
$stmtsKey = $bareServiceSetMethodCall->getAttribute(AttributeKey::STMT_KEY);
unset($node->stmts[$stmtsKey]);
}
return $node;
}
@ -138,35 +141,36 @@ CODE_SAMPLE
return $firstArg->value instanceof ClassConstFetch;
}
/**
* @return MethodCall[]
* @return array<Expression<MethodCall>>
*/
private function collectServiceSetMethodCalls(Closure $closure) : array
private function collectServiceSetMethodCallExpressions(Closure $closure) : array
{
$servicesSetMethodCalls = [];
$this->traverseNodesWithCallable($closure, function (Node $node) use(&$servicesSetMethodCalls) {
if (!$node instanceof Expression) {
return null;
foreach ($closure->stmts as $stmt) {
if (!$stmt instanceof Expression) {
continue;
}
if (!$node->expr instanceof MethodCall) {
return null;
if (!$stmt->expr instanceof MethodCall) {
continue;
}
$methodCall = $node->expr;
$methodCall = $stmt->expr;
if (!$this->isBareServicesSetMethodCall($methodCall)) {
return null;
continue;
}
$servicesSetMethodCalls[] = $methodCall;
return null;
});
$servicesSetMethodCalls[] = $stmt;
}
return $servicesSetMethodCalls;
}
/**
* @param MethodCall[] $methodsCalls
* @param array<Expression<MethodCall>> $methodsCallExpressions
* @return ClassNameAndFilePath[]
*/
private function createClassNamesAndFilePaths(array $methodsCalls) : array
private function createClassNamesAndFilePaths(array $methodsCallExpressions) : array
{
$classNamesAndFilesPaths = [];
foreach ($methodsCalls as $methodCall) {
foreach ($methodsCallExpressions as $methodCallExpression) {
/** @var MethodCall $methodCall */
$methodCall = $methodCallExpression->expr;
$firstArg = $methodCall->getArgs()[0];
$serviceClassReference = $this->valueResolver->getValue($firstArg->value);
if (!\is_string($serviceClassReference)) {
@ -191,4 +195,9 @@ CODE_SAMPLE
$distConstFetch = new ConstFetch(new Name('__DIR__'));
return new Concat($distConstFetch, new String_('/' . $relativeDirectoryPath));
}
private function createServicesLoadMethodCall(string $sharedNamespace, Concat $directoryConcat) : MethodCall
{
$args = [new Arg(new String_($sharedNamespace)), new Arg($directoryConcat)];
return new MethodCall(new Variable('services'), 'load', $args);
}
}

View File

@ -1,114 +0,0 @@
<?php
declare (strict_types=1);
namespace Rector\Symfony\Rector\Property;
use PhpParser\Node;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\DependencyInjection\NodeManipulator\PropertyConstructorInjectionManipulator;
use Rector\Symfony\TypeAnalyzer\JMSDITypeResolver;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* Can cover these cases:
* - https://jmsyst.com/bundles/JMSDiExtraBundle/master/annotations
* - https://github.com/rectorphp/rector/issues/700#issue-370301169
*
* @see \Rector\Symfony\Tests\Rector\Property\JMSInjectPropertyToConstructorInjectionRector\JMSInjectPropertyToConstructorInjectionRectorTest
*/
final class JMSInjectPropertyToConstructorInjectionRector extends AbstractRector
{
/**
* @var string
*/
private const INJECT_ANNOTATION_CLASS = 'JMS\\DiExtraBundle\\Annotation\\Inject';
/**
* @readonly
* @var \Rector\Symfony\TypeAnalyzer\JMSDITypeResolver
*/
private $jmsDITypeResolver;
/**
* @readonly
* @var \Rector\DependencyInjection\NodeManipulator\PropertyConstructorInjectionManipulator
*/
private $propertyConstructorInjectionManipulator;
/**
* @readonly
* @var \Rector\Core\Php\PhpVersionProvider
*/
private $phpVersionProvider;
public function __construct(JMSDITypeResolver $jmsDITypeResolver, PropertyConstructorInjectionManipulator $propertyConstructorInjectionManipulator, PhpVersionProvider $phpVersionProvider)
{
$this->jmsDITypeResolver = $jmsDITypeResolver;
$this->propertyConstructorInjectionManipulator = $propertyConstructorInjectionManipulator;
$this->phpVersionProvider = $phpVersionProvider;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('Turns properties with `@inject` to private properties and constructor injection', [new CodeSample(<<<'CODE_SAMPLE'
/**
* @var SomeService
* @inject
*/
public $someService;
CODE_SAMPLE
, <<<'CODE_SAMPLE'
/**
* @var SomeService
*/
private $someService;
public function __construct(SomeService $someService)
{
$this->someService = $someService;
}
CODE_SAMPLE
)]);
}
/**
* @return array<class-string<Node>>
*/
public function getNodeTypes() : array
{
return [Property::class];
}
/**
* @param Property $node
*/
public function refactor(Node $node) : ?Node
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
if (!$phpDocInfo instanceof PhpDocInfo) {
return null;
}
$doctrineAnnotationTagValueNode = $phpDocInfo->getByAnnotationClass(self::INJECT_ANNOTATION_CLASS);
if (!$doctrineAnnotationTagValueNode instanceof DoctrineAnnotationTagValueNode) {
return null;
}
$serviceType = $this->resolveServiceType($doctrineAnnotationTagValueNode, $phpDocInfo, $node);
if ($serviceType instanceof MixedType) {
return null;
}
$this->propertyConstructorInjectionManipulator->refactor($node, $serviceType, $doctrineAnnotationTagValueNode);
if ($this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) {
$this->removeNode($node);
return null;
}
return $node;
}
private function resolveServiceType(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, PhpDocInfo $phpDocInfo, Property $property) : Type
{
$serviceType = $phpDocInfo->getVarType();
if (!$serviceType instanceof MixedType) {
return $serviceType;
}
return $this->jmsDITypeResolver->resolve($property, $doctrineAnnotationTagValueNode);
}
}

View File

@ -9,10 +9,6 @@ use Rector\Set\Contract\SetListInterface;
*/
final class JMSSetList implements SetListInterface
{
/**
* @var string
*/
public const REMOVE_JMS_INJECT = __DIR__ . '/../../config/sets/jms/remove-jms-inject.php';
/**
* @var string
*/

View File

@ -1,126 +0,0 @@
<?php
declare (strict_types=1);
namespace Rector\Symfony\TypeAnalyzer;
use PhpParser\Node\Stmt\Property;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode;
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
use Rector\BetterPhpDocParser\PhpDoc\StringNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Provider\CurrentFileProvider;
use Rector\Core\ValueObject\Application\File;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\Symfony\DataProvider\ServiceMapProvider;
use Rector\Symfony\ValueObject\ServiceMap\ServiceMap;
final class JMSDITypeResolver
{
/**
* @readonly
* @var \Rector\Symfony\DataProvider\ServiceMapProvider
*/
private $serviceMapProvider;
/**
* @readonly
* @var \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory
*/
private $phpDocInfoFactory;
/**
* @readonly
* @var \PHPStan\Reflection\ReflectionProvider
*/
private $reflectionProvider;
/**
* @readonly
* @var \Rector\NodeNameResolver\NodeNameResolver
*/
private $nodeNameResolver;
/**
* @readonly
* @var \Rector\Core\Provider\CurrentFileProvider
*/
private $currentFileProvider;
public function __construct(ServiceMapProvider $serviceMapProvider, PhpDocInfoFactory $phpDocInfoFactory, ReflectionProvider $reflectionProvider, NodeNameResolver $nodeNameResolver, CurrentFileProvider $currentFileProvider)
{
$this->serviceMapProvider = $serviceMapProvider;
$this->phpDocInfoFactory = $phpDocInfoFactory;
$this->reflectionProvider = $reflectionProvider;
$this->nodeNameResolver = $nodeNameResolver;
$this->currentFileProvider = $currentFileProvider;
}
public function resolve(Property $property, DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode) : Type
{
$serviceMap = $this->serviceMapProvider->provide();
$serviceName = $this->resolveServiceName($doctrineAnnotationTagValueNode, $property);
$serviceType = $this->resolveFromServiceName($serviceName, $serviceMap);
if (!$serviceType instanceof MixedType) {
return $serviceType;
}
// 3. service is in @var annotation
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($property);
$varType = $phpDocInfo->getVarType();
if (!$varType instanceof MixedType) {
return $varType;
}
// the @var is missing and service name was not found → report it
$this->reportServiceNotFound($serviceName);
return new MixedType();
}
private function reportServiceNotFound(?string $serviceName) : void
{
if ($serviceName !== null) {
return;
}
$file = $this->currentFileProvider->getFile();
if (!$file instanceof File) {
throw new ShouldNotHappenException();
}
$errorMessage = \sprintf('Service "%s" was not found in DI Container of your Symfony App.', $serviceName);
throw new ShouldNotHappenException($errorMessage);
}
private function resolveFromServiceName(string $serviceName, ServiceMap $serviceMap) : Type
{
// 1. service name-type
if ($this->reflectionProvider->hasClass($serviceName)) {
// single class service
return new ObjectType($serviceName);
}
// 2. service name
if ($serviceMap->hasService($serviceName)) {
$serviceType = $serviceMap->getServiceType($serviceName);
if ($serviceType instanceof Type) {
return $serviceType;
}
}
return new MixedType();
}
private function resolveServiceName(DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode, Property $property) : string
{
$serviceNameParameter = $doctrineAnnotationTagValueNode->getValue('serviceName');
if ($serviceNameParameter instanceof ArrayItemNode) {
$serviceNameParameterValue = $serviceNameParameter->value;
if ($serviceNameParameterValue instanceof StringNode) {
$serviceNameParameterValue = $serviceNameParameterValue->value;
}
if (\is_string($serviceNameParameterValue)) {
return $serviceNameParameterValue;
}
}
$arrayItemNode = $doctrineAnnotationTagValueNode->getSilentValue();
if ($arrayItemNode instanceof ArrayItemNode) {
$arrayItemNodeValue = $arrayItemNode->value;
if ($arrayItemNodeValue instanceof StringNode) {
$arrayItemNodeValue = $arrayItemNodeValue->value;
}
if (\is_string($arrayItemNodeValue)) {
return $arrayItemNodeValue;
}
}
return $this->nodeNameResolver->getName($property);
}
}

View File

@ -20,6 +20,9 @@ final class ServiceMap
{
$this->services = $services;
}
/**
* @api
*/
public function hasService(string $id) : bool
{
return isset($this->services[$id]);