arrayCallableMethodMatcher = $arrayCallableMethodMatcher; $this->reflectionProvider = $reflectionProvider; } public function getRuleDefinition() : RuleDefinition { return new RuleDefinition('Upgrade array callable to first class callable', [new CodeSample(<<<'CODE_SAMPLE' final class SomeClass { public function run() { $name = [$this, 'name']; } public function name() { } } CODE_SAMPLE , <<<'CODE_SAMPLE' final class SomeClass { public function run() { $name = $this->name(...); } public function name() { } } CODE_SAMPLE )]); } /** * @return array> */ public function getNodeTypes() : array { return [Property::class, ClassConst::class, Array_::class]; } /** * @param Property|ClassConst|Array_ $node * @return int|null|\PhpParser\Node\Expr\StaticCall|\PhpParser\Node\Expr\MethodCall */ public function refactorWithScope(Node $node, Scope $scope) { if ($node instanceof Property || $node instanceof ClassConst) { return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN; } $arrayCallable = $this->arrayCallableMethodMatcher->match($node, $scope); if (!$arrayCallable instanceof ArrayCallable) { return null; } $callerExpr = $arrayCallable->getCallerExpr(); if (!$callerExpr instanceof Variable && !$callerExpr instanceof PropertyFetch && !$callerExpr instanceof ClassConstFetch) { return null; } $args = [new VariadicPlaceholder()]; if ($callerExpr instanceof ClassConstFetch) { $type = $this->getType($callerExpr->class); if ($type instanceof FullyQualifiedObjectType && $this->isNonStaticOtherObject($type, $arrayCallable, $scope)) { return null; } return new StaticCall($callerExpr->class, $arrayCallable->getMethod(), $args); } return new MethodCall($callerExpr, $arrayCallable->getMethod(), $args); } public function provideMinPhpVersion() : int { return PhpVersion::PHP_81; } private function isNonStaticOtherObject(FullyQualifiedObjectType $fullyQualifiedObjectType, ArrayCallable $arrayCallable, Scope $scope) : bool { $classReflection = $scope->getClassReflection(); if ($classReflection instanceof ClassReflection && $classReflection->getName() === $fullyQualifiedObjectType->getClassName()) { return \false; } $arrayClassReflection = $this->reflectionProvider->getClass($arrayCallable->getClass()); // we're unable to find it if (!$arrayClassReflection->hasMethod($arrayCallable->getMethod())) { return \false; } $extendedMethodReflection = $arrayClassReflection->getMethod($arrayCallable->getMethod(), $scope); if (!$extendedMethodReflection->isStatic()) { return \true; } return !$extendedMethodReflection->isPublic(); } }