From adf5a2880969beae15bdeef712bcef96407d59f1 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sun, 22 Jan 2017 17:10:41 +0100 Subject: [PATCH] Closure::__invoke is not in PHP reflection --- .../Php/PhpClassReflectionExtension.php | 15 ++++++++++- src/Reflection/Php/PhpMethodReflection.php | 25 +++++++++++++++++-- .../Rules/Methods/data/call-methods.php | 5 ++++ 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/Reflection/Php/PhpClassReflectionExtension.php b/src/Reflection/Php/PhpClassReflectionExtension.php index 1b4c8490f6..bb425e57c0 100644 --- a/src/Reflection/Php/PhpClassReflectionExtension.php +++ b/src/Reflection/Php/PhpClassReflectionExtension.php @@ -137,7 +137,20 @@ public function getMethod(ClassReflection $classReflection, string $methodName): private function createMethods(ClassReflection $classReflection): array { $methods = []; - foreach ($classReflection->getNativeReflection()->getMethods() as $methodReflection) { + $reflectionMethods = $classReflection->getNativeReflection()->getMethods(); + if ($classReflection->getName() === \Closure::class || $classReflection->isSubclassOf(\Closure::class)) { + $hasInvokeMethod = false; + foreach ($reflectionMethods as $reflectionMethod) { + if ($reflectionMethod->getName() === '__invoke') { + $hasInvokeMethod = true; + break; + } + } + if (!$hasInvokeMethod) { + $reflectionMethods[] = $classReflection->getNativeReflection()->getMethod('__invoke'); + } + } + foreach ($reflectionMethods as $methodReflection) { $declaringClass = $this->broker->getClass($methodReflection->getDeclaringClass()->getName()); $phpDocParameterTypes = []; diff --git a/src/Reflection/Php/PhpMethodReflection.php b/src/Reflection/Php/PhpMethodReflection.php index d5280061bc..85f38c0b65 100644 --- a/src/Reflection/Php/PhpMethodReflection.php +++ b/src/Reflection/Php/PhpMethodReflection.php @@ -189,6 +189,19 @@ public function getParameters(): array true ); } + if ( + $this->declaringClass->getName() === 'Closure' + && $this->reflection->getName() === '__invoke' + && count($this->parameters) < 1 + ) { + $this->parameters[] = new DummyParameter( + 'args', + new MixedType(), + true, + false, + true + ); + } } return $this->parameters; @@ -199,8 +212,16 @@ public function isVariadic(): bool $isNativelyVariadic = $this->reflection->isVariadic(); if ( !$isNativelyVariadic - && $this->declaringClass->getName() === 'ReflectionMethod' - && $this->reflection->getName() === 'invoke' + && ( + ( + $this->declaringClass->getName() === 'ReflectionMethod' + && $this->reflection->getName() === 'invoke' + ) + || ( + $this->declaringClass->getName() === 'Closure' + && $this->reflection->getName() === '__invoke' + ) + ) ) { return true; } diff --git a/tests/PHPStan/Rules/Methods/data/call-methods.php b/tests/PHPStan/Rules/Methods/data/call-methods.php index d01aa3a7e5..0bf65ba3c1 100644 --- a/tests/PHPStan/Rules/Methods/data/call-methods.php +++ b/tests/PHPStan/Rules/Methods/data/call-methods.php @@ -133,4 +133,9 @@ public function acceptsString(string $foo) function () { $foo = new ClassWithToString(); $foo->acceptsString($foo); + + $closure = function () { + + }; + $closure->__invoke(1, 2, 3); };