mirror of https://github.com/rectorphp/rector.git
Updated Rector to commit a61fbf265d2995453ad8d9f12b6bb00ff1937162
a61fbf265d
Remove removeNode() from couple rules (#4083)
This commit is contained in:
parent
1078587eae
commit
bc7911e5d1
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -22,4 +22,4 @@ if (PHP_VERSION_ID < 50600) {
|
|||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit235df547e969478b4de18a8fbf52548b::getLoader();
|
||||
return ComposerAutoloaderInit3a11c776258046af5ada1aa5e6635840::getLoader();
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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';
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -20,6 +20,9 @@ final class ServiceMap
|
|||
{
|
||||
$this->services = $services;
|
||||
}
|
||||
/**
|
||||
* @api
|
||||
*/
|
||||
public function hasService(string $id) : bool
|
||||
{
|
||||
return isset($this->services[$id]);
|
||||
|
|
Loading…
Reference in New Issue