diff --git a/src/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php b/src/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php index d655ca56113..7f3a021e810 100644 --- a/src/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php +++ b/src/NodeCollector/NodeAnalyzer/ArrayCallableMethodMatcher.php @@ -72,6 +72,10 @@ public function match( return null; } + if ($array->getAttribute(AttributeKey::IS_ARG_VALUE_FACTORY_SERVICECONFIGURATOR) === true) { + return null; + } + $values = $this->valueResolver->getValue($array); $className = $callerType->getClassName(); $secondItemValue = $items[1]->value; diff --git a/src/NodeTypeResolver/Node/AttributeKey.php b/src/NodeTypeResolver/Node/AttributeKey.php index e2256a4256f..080d8f776bd 100644 --- a/src/NodeTypeResolver/Node/AttributeKey.php +++ b/src/NodeTypeResolver/Node/AttributeKey.php @@ -166,4 +166,6 @@ final class AttributeKey public const string HAS_CLOSURE_WITH_VARIADIC_ARGS = 'has_closure_with_variadic_args'; public const string IS_IN_TRY_BLOCK = 'is_in_try_block'; + + public const string IS_ARG_VALUE_FACTORY_SERVICECONFIGURATOR = 'is_arg_value_factory_serviceconfigurator'; } diff --git a/src/PhpParser/NodeVisitor/ContextNodeVisitor.php b/src/PhpParser/NodeVisitor/ContextNodeVisitor.php index 4d147b6a9cb..4c9d86becbd 100644 --- a/src/PhpParser/NodeVisitor/ContextNodeVisitor.php +++ b/src/PhpParser/NodeVisitor/ContextNodeVisitor.php @@ -10,6 +10,7 @@ use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\BinaryOp\BooleanAnd; +use PhpParser\Node\Expr\CallLike; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Isset_; use PhpParser\Node\Expr\PostDec; @@ -19,6 +20,7 @@ use PhpParser\Node\Expr\PropertyFetch; use PhpParser\Node\Expr\StaticPropertyFetch; use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Identifier; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Param; use PhpParser\Node\Stmt\Break_; @@ -36,15 +38,20 @@ use PhpParser\Node\Stmt\While_; use PhpParser\NodeVisitor; use PhpParser\NodeVisitorAbstract; +use PHPStan\Analyser\Scope; +use PHPStan\Reflection\MethodReflection; use Rector\Contract\PhpParser\DecoratingNodeVisitorInterface; use Rector\NodeTypeResolver\Node\AttributeKey; +use Rector\NodeTypeResolver\PHPStan\ParametersAcceptorSelectorVariantsWrapper; use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; use Rector\PhpParser\NodeTraverser\SimpleNodeTraverser; +use Rector\Reflection\ReflectionResolver; final class ContextNodeVisitor extends NodeVisitorAbstract implements DecoratingNodeVisitorInterface { public function __construct( - private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser + private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser, + private readonly ReflectionResolver $reflectionResolver ) { } @@ -92,6 +99,52 @@ public function enterNode(Node $node): ?Node return null; } + if ($node instanceof CallLike && ! $node->isFirstClassCallable()) { + $functionReflection = $this->reflectionResolver->resolveFunctionLikeReflectionFromCall($node); + if (! $functionReflection instanceof MethodReflection) { + return null; + } + + if ($functionReflection->getDeclaringClass()->getName() !== 'Symfony\Component\DependencyInjection\Loader\Configurator\ServiceConfigurator') { + return null; + } + + if ($functionReflection->getName() !== 'factory') { + return null; + } + + $scope = $node->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return null; + } + + $parametersAcceptor = ParametersAcceptorSelectorVariantsWrapper::select( + $functionReflection, + $node, + $scope + ); + + $args = $node->getArgs(); + foreach ($parametersAcceptor->getParameters() as $key => $parameterReflection) { + if ($parameterReflection->getName() !== 'factory') { + continue; + } + + // based on name + foreach ($args as $arg) { + if ($arg->name instanceof Identifier && 'factory' === $arg->name->toString()) { + $arg->value->setAttribute(AttributeKey::IS_ARG_VALUE_FACTORY_SERVICECONFIGURATOR, true); + continue 2; + } + } + + // based on key + if (isset($args[$key])) { + $args[$key]->value->setAttribute(AttributeKey::IS_ARG_VALUE_FACTORY_SERVICECONFIGURATOR, true); + } + } + } + if ($node instanceof Arg) { $node->value->setAttribute(AttributeKey::IS_ARG_VALUE, true); return null; diff --git a/stubs/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php b/stubs/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php new file mode 100644 index 00000000000..3c3fb6ad8a5 --- /dev/null +++ b/stubs/Symfony/Component/DependencyInjection/Loader/Configurator/ContainerConfigurator.php @@ -0,0 +1,18 @@ +