From df1bf6af9ec9078f12aaf9fe83bddc7f44ecc0c0 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 21 Nov 2022 21:54:04 +0100 Subject: [PATCH 01/10] Introduce classImplements dynamic return type --- conf/config.neon | 5 ++ ...sImplementsFunctionReturnTypeExtension.php | 60 +++++++++++++++++++ .../Analyser/NodeScopeResolverTest.php | 1 + .../Analyser/data/class-implements.php | 33 ++++++++++ 4 files changed, 99 insertions(+) create mode 100644 src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php create mode 100644 tests/PHPStan/Analyser/data/class-implements.php diff --git a/conf/config.neon b/conf/config.neon index fb534ed239..15a8482007 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -1637,6 +1637,11 @@ services: tags: - phpstan.typeSpecifier.functionTypeSpecifyingExtension + - + class: PHPStan\Type\Php\ClassImplementsFunctionReturnTypeExtension + tags: + - phpstan.broker.dynamicFunctionReturnTypeExtension + - class: PHPStan\Type\Php\DefineConstantTypeSpecifyingExtension tags: diff --git a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php new file mode 100644 index 0000000000..728ff83142 --- /dev/null +++ b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php @@ -0,0 +1,60 @@ +getName() === 'class_implements'; + } + + public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type + { + if (count($functionCall->getArgs()) < 1) { + return null; + } + + $objectOrClassType = $scope->getType($functionCall->getArgs()[0]->value); + $autoload = !isset($functionCall->getArgs()[1]) + || $scope->getType($functionCall->getArgs()[1]->value)->equals(new ConstantBooleanType(true)); + + if ($objectOrClassType instanceof UnionType) { + foreach ($objectOrClassType->getTypes() as $type) { + if ($this->canFunctionReturnFalse($type, $autoload)) { + return null; + } + } + } elseif ($this->canFunctionReturnFalse($objectOrClassType, $autoload)) { + return null; + } + + return TypeCombinator::remove( + ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType(), + new ConstantBooleanType(false), + ); + } + + private function canFunctionReturnFalse(Type $type, bool $autoload): bool + { + return !$type instanceof ObjectType + && !$type instanceof ObjectWithoutClassType + && (!$autoload || !$type instanceof ClassStringType); + } +} diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index eb53bed1ef..b1eb3f399f 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -492,6 +492,7 @@ public function dataFileAsserts(): iterable } yield from $this->gatherAssertTypes(__DIR__ . '/data/class-constant-types.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/class-implements.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3379.php'); diff --git a/tests/PHPStan/Analyser/data/class-implements.php b/tests/PHPStan/Analyser/data/class-implements.php new file mode 100644 index 0000000000..949cde5156 --- /dev/null +++ b/tests/PHPStan/Analyser/data/class-implements.php @@ -0,0 +1,33 @@ +', class_implements($object)); + assertType('array', class_implements($objectOrClassString)); + assertType('array|false', class_implements($objectOrString)); + assertType('array', class_implements($classString)); + assertType('array|false', class_implements($className)); + + assertType('array', class_implements($object, false)); + assertType('array|false', class_implements($objectOrClassString, false)); + assertType('array|false', class_implements($objectOrString, false)); + assertType('array|false', class_implements($classString, false)); + assertType('array|false', class_implements($className, false)); + } + +} From 4e976618f991ebc6ebf0f4da1dc46a932bdb1ff9 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 21 Nov 2022 22:00:06 +0100 Subject: [PATCH 02/10] Add more support --- ...sImplementsFunctionReturnTypeExtension.php | 6 ++++- .../Analyser/data/class-implements.php | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php index 728ff83142..1c5ae79c11 100644 --- a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php +++ b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php @@ -22,7 +22,11 @@ class ClassImplementsFunctionReturnTypeExtension implements DynamicFunctionRetur public function isFunctionSupported(FunctionReflection $functionReflection): bool { - return $functionReflection->getName() === 'class_implements'; + return in_array( + $functionReflection->getName(), + ['class_implements', 'class_uses', 'class_parents'], + true, + ); } public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type diff --git a/tests/PHPStan/Analyser/data/class-implements.php b/tests/PHPStan/Analyser/data/class-implements.php index 949cde5156..07686e3f71 100644 --- a/tests/PHPStan/Analyser/data/class-implements.php +++ b/tests/PHPStan/Analyser/data/class-implements.php @@ -28,6 +28,30 @@ public function test( assertType('array|false', class_implements($objectOrString, false)); assertType('array|false', class_implements($classString, false)); assertType('array|false', class_implements($className, false)); + + assertType('array', class_uses($object)); + assertType('array', class_uses($objectOrClassString)); + assertType('array|false', class_uses($objectOrString)); + assertType('array', class_uses($classString)); + assertType('array|false', class_uses($className)); + + assertType('array', class_uses($object, false)); + assertType('array|false', class_uses($objectOrClassString, false)); + assertType('array|false', class_uses($objectOrString, false)); + assertType('array|false', class_uses($classString, false)); + assertType('array|false', class_uses($className, false)); + + assertType('array', class_parents($object)); + assertType('array', class_parents($objectOrClassString)); + assertType('array|false', class_parents($objectOrString)); + assertType('array', class_parents($classString)); + assertType('array|false', class_parents($className)); + + assertType('array', class_parents($object, false)); + assertType('array|false', class_parents($objectOrClassString, false)); + assertType('array|false', class_parents($objectOrString, false)); + assertType('array|false', class_parents($classString, false)); + assertType('array|false', class_parents($className, false)); } } From 656648ad4afaccbaead34405b0b13980a5e965da Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 21 Nov 2022 22:01:35 +0100 Subject: [PATCH 03/10] Fix cs --- src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php index 1c5ae79c11..0eb2853362 100644 --- a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php +++ b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php @@ -14,8 +14,8 @@ use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\UnionType; - use function count; +use function in_array; class ClassImplementsFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension { @@ -61,4 +61,5 @@ private function canFunctionReturnFalse(Type $type, bool $autoload): bool && !$type instanceof ObjectWithoutClassType && (!$autoload || !$type instanceof ClassStringType); } + } From 5ea7a8a52efc016366802e7e6f3ed5160e7c0bbb Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 21 Nov 2022 22:06:47 +0100 Subject: [PATCH 04/10] Prefer to use method --- src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php index 0eb2853362..f1b59ccefa 100644 --- a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php +++ b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php @@ -6,7 +6,6 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\ParametersAcceptorSelector; -use PHPStan\Type\ClassStringType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; use PHPStan\Type\ObjectType; @@ -59,7 +58,7 @@ private function canFunctionReturnFalse(Type $type, bool $autoload): bool { return !$type instanceof ObjectType && !$type instanceof ObjectWithoutClassType - && (!$autoload || !$type instanceof ClassStringType); + && (!$autoload || !$type->isClassStringType()->yes()); } } From 6a3e0e380c1a69716b296495ef7d9763ef76bd79 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 1 Dec 2022 16:59:42 +0100 Subject: [PATCH 05/10] Review --- src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php index f1b59ccefa..b48f73d901 100644 --- a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php +++ b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php @@ -8,7 +8,6 @@ use PHPStan\Reflection\ParametersAcceptorSelector; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; -use PHPStan\Type\ObjectType; use PHPStan\Type\ObjectWithoutClassType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; @@ -56,8 +55,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, private function canFunctionReturnFalse(Type $type, bool $autoload): bool { - return !$type instanceof ObjectType - && !$type instanceof ObjectWithoutClassType + return (new ObjectWithoutClassType())->isSuperTypeOf($type)->no() && (!$autoload || !$type->isClassStringType()->yes()); } From f36ad4081476d071ac01de233f3d0a639da377ef Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 9 Jan 2023 22:24:55 +0100 Subject: [PATCH 06/10] More tests --- .../Analyser/data/class-implements.php | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/data/class-implements.php b/tests/PHPStan/Analyser/data/class-implements.php index 07686e3f71..c96a9f32b4 100644 --- a/tests/PHPStan/Analyser/data/class-implements.php +++ b/tests/PHPStan/Analyser/data/class-implements.php @@ -15,7 +15,9 @@ public function test( object|string $objectOrClassString, object|string $objectOrString, string $classString, - string $className + string $className, + bool $bool, + mixed $mixed, ): void { assertType('array', class_implements($object)); assertType('array', class_implements($objectOrClassString)); @@ -23,35 +25,89 @@ public function test( assertType('array', class_implements($classString)); assertType('array|false', class_implements($className)); + assertType('array', class_implements($object, true)); + assertType('array', class_implements($objectOrClassString, true)); + assertType('array|false', class_implements($objectOrString, true)); + assertType('array', class_implements($classString, true)); + assertType('array|false', class_implements($className, true)); + assertType('array', class_implements($object, false)); assertType('array|false', class_implements($objectOrClassString, false)); assertType('array|false', class_implements($objectOrString, false)); assertType('array|false', class_implements($classString, false)); assertType('array|false', class_implements($className, false)); + assertType('array', class_implements($object, $bool)); + assertType('array|false', class_implements($objectOrClassString, $bool)); + assertType('array|false', class_implements($objectOrString, $bool)); + assertType('array|false', class_implements($classString, $bool)); + assertType('array|false', class_implements($className, $bool)); + + assertType('array', class_implements($object, $mixed)); + assertType('array|false', class_implements($objectOrClassString, $mixed)); + assertType('array|false', class_implements($objectOrString, $mixed)); + assertType('array|false', class_implements($classString, $mixed)); + assertType('array|false', class_implements($className, $mixed)); + assertType('array', class_uses($object)); assertType('array', class_uses($objectOrClassString)); assertType('array|false', class_uses($objectOrString)); assertType('array', class_uses($classString)); assertType('array|false', class_uses($className)); + assertType('array', class_uses($object, true)); + assertType('array', class_uses($objectOrClassString, true)); + assertType('array|false', class_uses($objectOrString, true)); + assertType('array', class_uses($classString, true)); + assertType('array|false', class_uses($className, true)); + assertType('array', class_uses($object, false)); assertType('array|false', class_uses($objectOrClassString, false)); assertType('array|false', class_uses($objectOrString, false)); assertType('array|false', class_uses($classString, false)); assertType('array|false', class_uses($className, false)); + assertType('array', class_uses($object, $bool)); + assertType('array|false', class_uses($objectOrClassString, $bool)); + assertType('array|false', class_uses($objectOrString, $bool)); + assertType('array|false', class_uses($classString, $bool)); + assertType('array|false', class_uses($className, $bool)); + + assertType('array', class_uses($object, $mixed)); + assertType('array|false', class_uses($objectOrClassString, $mixed)); + assertType('array|false', class_uses($objectOrString, $mixed)); + assertType('array|false', class_uses($classString, $mixed)); + assertType('array|false', class_uses($className, $mixed)); + assertType('array', class_parents($object)); assertType('array', class_parents($objectOrClassString)); assertType('array|false', class_parents($objectOrString)); assertType('array', class_parents($classString)); assertType('array|false', class_parents($className)); + assertType('array', class_parents($object, true)); + assertType('array', class_parents($objectOrClassString, true)); + assertType('array|false', class_parents($objectOrString, true)); + assertType('array', class_parents($classString, true)); + assertType('array|false', class_parents($className, true)); + assertType('array', class_parents($object, false)); assertType('array|false', class_parents($objectOrClassString, false)); assertType('array|false', class_parents($objectOrString, false)); assertType('array|false', class_parents($classString, false)); assertType('array|false', class_parents($className, false)); + + assertType('array', class_parents($object, $bool)); + assertType('array|false', class_parents($objectOrClassString, $bool)); + assertType('array|false', class_parents($objectOrString, $bool)); + assertType('array|false', class_parents($classString, $bool)); + assertType('array|false', class_parents($className, $bool)); + + assertType('array', class_parents($object, $mixed)); + assertType('array|false', class_parents($objectOrClassString, $mixed)); + assertType('array|false', class_parents($objectOrString, $mixed)); + assertType('array|false', class_parents($classString, $mixed)); + assertType('array|false', class_parents($className, $mixed)); } } From b75ff20d972d3cfcf68b10461120d47954f07169 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 9 Jan 2023 23:35:28 +0100 Subject: [PATCH 07/10] Improvements --- ...sImplementsFunctionReturnTypeExtension.php | 35 +++++++++---------- .../Analyser/data/class-implements.php | 15 ++++++++ 2 files changed, 32 insertions(+), 18 deletions(-) diff --git a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php index b48f73d901..879e4d3e67 100644 --- a/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php +++ b/src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php @@ -6,6 +6,7 @@ use PHPStan\Analyser\Scope; use PHPStan\Reflection\FunctionReflection; use PHPStan\Reflection\ParametersAcceptorSelector; +use PHPStan\Type\ClassStringType; use PHPStan\Type\Constant\ConstantBooleanType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; use PHPStan\Type\ObjectWithoutClassType; @@ -33,30 +34,28 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, return null; } - $objectOrClassType = $scope->getType($functionCall->getArgs()[0]->value); + $firstArgType = $scope->getType($functionCall->getArgs()[0]->value); $autoload = !isset($functionCall->getArgs()[1]) || $scope->getType($functionCall->getArgs()[1]->value)->equals(new ConstantBooleanType(true)); - if ($objectOrClassType instanceof UnionType) { - foreach ($objectOrClassType->getTypes() as $type) { - if ($this->canFunctionReturnFalse($type, $autoload)) { - return null; - } - } - } elseif ($this->canFunctionReturnFalse($objectOrClassType, $autoload)) { - return null; + $isObject = (new ObjectWithoutClassType())->isSuperTypeOf($firstArgType); + + $objectOrClassString = (new UnionType([new ObjectWithoutClassType(), new ClassStringType()])); + if ( + $autoload && $objectOrClassString->isSuperTypeOf($firstArgType)->yes() + || $isObject->yes() + ) { + return TypeCombinator::remove( + ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType(), + new ConstantBooleanType(false), + ); } - return TypeCombinator::remove( - ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType(), - new ConstantBooleanType(false), - ); - } + if ($isObject->no() && $firstArgType->isClassStringType()->no()) { + return new ConstantBooleanType(false); + } - private function canFunctionReturnFalse(Type $type, bool $autoload): bool - { - return (new ObjectWithoutClassType())->isSuperTypeOf($type)->no() - && (!$autoload || !$type->isClassStringType()->yes()); + return null; } } diff --git a/tests/PHPStan/Analyser/data/class-implements.php b/tests/PHPStan/Analyser/data/class-implements.php index c96a9f32b4..acf77827d5 100644 --- a/tests/PHPStan/Analyser/data/class-implements.php +++ b/tests/PHPStan/Analyser/data/class-implements.php @@ -24,90 +24,105 @@ public function test( assertType('array|false', class_implements($objectOrString)); assertType('array', class_implements($classString)); assertType('array|false', class_implements($className)); + assertType('false', class_implements('thisIsNotAClass')); assertType('array', class_implements($object, true)); assertType('array', class_implements($objectOrClassString, true)); assertType('array|false', class_implements($objectOrString, true)); assertType('array', class_implements($classString, true)); assertType('array|false', class_implements($className, true)); + assertType('false', class_implements('thisIsNotAClass', true)); assertType('array', class_implements($object, false)); assertType('array|false', class_implements($objectOrClassString, false)); assertType('array|false', class_implements($objectOrString, false)); assertType('array|false', class_implements($classString, false)); assertType('array|false', class_implements($className, false)); + assertType('false', class_implements('thisIsNotAClass', false)); assertType('array', class_implements($object, $bool)); assertType('array|false', class_implements($objectOrClassString, $bool)); assertType('array|false', class_implements($objectOrString, $bool)); assertType('array|false', class_implements($classString, $bool)); assertType('array|false', class_implements($className, $bool)); + assertType('false', class_implements('thisIsNotAClass', $bool)); assertType('array', class_implements($object, $mixed)); assertType('array|false', class_implements($objectOrClassString, $mixed)); assertType('array|false', class_implements($objectOrString, $mixed)); assertType('array|false', class_implements($classString, $mixed)); assertType('array|false', class_implements($className, $mixed)); + assertType('false', class_implements('thisIsNotAClass', $mixed)); assertType('array', class_uses($object)); assertType('array', class_uses($objectOrClassString)); assertType('array|false', class_uses($objectOrString)); assertType('array', class_uses($classString)); assertType('array|false', class_uses($className)); + assertType('false', class_uses('thisIsNotAClass')); assertType('array', class_uses($object, true)); assertType('array', class_uses($objectOrClassString, true)); assertType('array|false', class_uses($objectOrString, true)); assertType('array', class_uses($classString, true)); assertType('array|false', class_uses($className, true)); + assertType('false', class_uses('thisIsNotAClass', true)); assertType('array', class_uses($object, false)); assertType('array|false', class_uses($objectOrClassString, false)); assertType('array|false', class_uses($objectOrString, false)); assertType('array|false', class_uses($classString, false)); assertType('array|false', class_uses($className, false)); + assertType('false', class_uses('thisIsNotAClass', false)); assertType('array', class_uses($object, $bool)); assertType('array|false', class_uses($objectOrClassString, $bool)); assertType('array|false', class_uses($objectOrString, $bool)); assertType('array|false', class_uses($classString, $bool)); assertType('array|false', class_uses($className, $bool)); + assertType('false', class_uses('thisIsNotAClass', $bool)); assertType('array', class_uses($object, $mixed)); assertType('array|false', class_uses($objectOrClassString, $mixed)); assertType('array|false', class_uses($objectOrString, $mixed)); assertType('array|false', class_uses($classString, $mixed)); assertType('array|false', class_uses($className, $mixed)); + assertType('false', class_uses('thisIsNotAClass', $mixed)); assertType('array', class_parents($object)); assertType('array', class_parents($objectOrClassString)); assertType('array|false', class_parents($objectOrString)); assertType('array', class_parents($classString)); assertType('array|false', class_parents($className)); + assertType('false', class_parents('thisIsNotAClass', $className)); assertType('array', class_parents($object, true)); assertType('array', class_parents($objectOrClassString, true)); assertType('array|false', class_parents($objectOrString, true)); assertType('array', class_parents($classString, true)); assertType('array|false', class_parents($className, true)); + assertType('false', class_parents('thisIsNotAClass', true)); assertType('array', class_parents($object, false)); assertType('array|false', class_parents($objectOrClassString, false)); assertType('array|false', class_parents($objectOrString, false)); assertType('array|false', class_parents($classString, false)); assertType('array|false', class_parents($className, false)); + assertType('false', class_parents('thisIsNotAClass', false)); assertType('array', class_parents($object, $bool)); assertType('array|false', class_parents($objectOrClassString, $bool)); assertType('array|false', class_parents($objectOrString, $bool)); assertType('array|false', class_parents($classString, $bool)); assertType('array|false', class_parents($className, $bool)); + assertType('false', class_parents('thisIsNotAClass', $bool)); assertType('array', class_parents($object, $mixed)); assertType('array|false', class_parents($objectOrClassString, $mixed)); assertType('array|false', class_parents($objectOrString, $mixed)); assertType('array|false', class_parents($classString, $mixed)); assertType('array|false', class_parents($className, $mixed)); + assertType('false', class_parents('thisIsNotAClass', $mixed)); } } From d818e885c23c10036604236bb9b9d80767236b4c Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 11 Jan 2023 10:28:23 +0100 Subject: [PATCH 08/10] Improvement --- resources/functionMap.php | 4 +- .../Analyser/data/class-implements.php | 100 +++++++++--------- 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/resources/functionMap.php b/resources/functionMap.php index 0b25c6784b..4efa675198 100644 --- a/resources/functionMap.php +++ b/resources/functionMap.php @@ -948,9 +948,9 @@ 'chunk_split' => ['string', 'str'=>'string', 'chunklen='=>'positive-int', 'ending='=>'string'], 'class_alias' => ['bool', 'user_class_name'=>'string', 'alias_name'=>'string', 'autoload='=>'bool'], 'class_exists' => ['bool', 'classname'=>'string', 'autoload='=>'bool'], -'class_implements' => ['array|false', 'what'=>'object|string', 'autoload='=>'bool'], +'class_implements' => ['array|false', 'what'=>'object|string', 'autoload='=>'bool'], 'class_parents' => ['array|false', 'instance'=>'object|string', 'autoload='=>'bool'], -'class_uses' => ['array|false', 'what'=>'object|string', 'autoload='=>'bool'], +'class_uses' => ['array|false', 'what'=>'object|string', 'autoload='=>'bool'], 'classkit_import' => ['array', 'filename'=>'string'], 'classkit_method_add' => ['bool', 'classname'=>'string', 'methodname'=>'string', 'args'=>'string', 'code'=>'string', 'flags='=>'int'], 'classkit_method_copy' => ['bool', 'dclass'=>'string', 'dmethod'=>'string', 'sclass'=>'string', 'smethod='=>'string'], diff --git a/tests/PHPStan/Analyser/data/class-implements.php b/tests/PHPStan/Analyser/data/class-implements.php index acf77827d5..a529e4e206 100644 --- a/tests/PHPStan/Analyser/data/class-implements.php +++ b/tests/PHPStan/Analyser/data/class-implements.php @@ -19,74 +19,74 @@ public function test( bool $bool, mixed $mixed, ): void { - assertType('array', class_implements($object)); - assertType('array', class_implements($objectOrClassString)); - assertType('array|false', class_implements($objectOrString)); - assertType('array', class_implements($classString)); - assertType('array|false', class_implements($className)); + assertType('array', class_implements($object)); + assertType('array', class_implements($objectOrClassString)); + assertType('array|false', class_implements($objectOrString)); + assertType('array', class_implements($classString)); + assertType('array|false', class_implements($className)); assertType('false', class_implements('thisIsNotAClass')); - assertType('array', class_implements($object, true)); - assertType('array', class_implements($objectOrClassString, true)); - assertType('array|false', class_implements($objectOrString, true)); - assertType('array', class_implements($classString, true)); - assertType('array|false', class_implements($className, true)); + assertType('array', class_implements($object, true)); + assertType('array', class_implements($objectOrClassString, true)); + assertType('array|false', class_implements($objectOrString, true)); + assertType('array', class_implements($classString, true)); + assertType('array|false', class_implements($className, true)); assertType('false', class_implements('thisIsNotAClass', true)); - assertType('array', class_implements($object, false)); - assertType('array|false', class_implements($objectOrClassString, false)); - assertType('array|false', class_implements($objectOrString, false)); - assertType('array|false', class_implements($classString, false)); - assertType('array|false', class_implements($className, false)); + assertType('array', class_implements($object, false)); + assertType('array|false', class_implements($objectOrClassString, false)); + assertType('array|false', class_implements($objectOrString, false)); + assertType('array|false', class_implements($classString, false)); + assertType('array|false', class_implements($className, false)); assertType('false', class_implements('thisIsNotAClass', false)); - assertType('array', class_implements($object, $bool)); - assertType('array|false', class_implements($objectOrClassString, $bool)); - assertType('array|false', class_implements($objectOrString, $bool)); - assertType('array|false', class_implements($classString, $bool)); - assertType('array|false', class_implements($className, $bool)); + assertType('array', class_implements($object, $bool)); + assertType('array|false', class_implements($objectOrClassString, $bool)); + assertType('array|false', class_implements($objectOrString, $bool)); + assertType('array|false', class_implements($classString, $bool)); + assertType('array|false', class_implements($className, $bool)); assertType('false', class_implements('thisIsNotAClass', $bool)); - assertType('array', class_implements($object, $mixed)); - assertType('array|false', class_implements($objectOrClassString, $mixed)); - assertType('array|false', class_implements($objectOrString, $mixed)); - assertType('array|false', class_implements($classString, $mixed)); - assertType('array|false', class_implements($className, $mixed)); + assertType('array', class_implements($object, $mixed)); + assertType('array|false', class_implements($objectOrClassString, $mixed)); + assertType('array|false', class_implements($objectOrString, $mixed)); + assertType('array|false', class_implements($classString, $mixed)); + assertType('array|false', class_implements($className, $mixed)); assertType('false', class_implements('thisIsNotAClass', $mixed)); - assertType('array', class_uses($object)); - assertType('array', class_uses($objectOrClassString)); - assertType('array|false', class_uses($objectOrString)); - assertType('array', class_uses($classString)); - assertType('array|false', class_uses($className)); + assertType('array', class_uses($object)); + assertType('array', class_uses($objectOrClassString)); + assertType('array|false', class_uses($objectOrString)); + assertType('array', class_uses($classString)); + assertType('array|false', class_uses($className)); assertType('false', class_uses('thisIsNotAClass')); - assertType('array', class_uses($object, true)); - assertType('array', class_uses($objectOrClassString, true)); - assertType('array|false', class_uses($objectOrString, true)); - assertType('array', class_uses($classString, true)); - assertType('array|false', class_uses($className, true)); + assertType('array', class_uses($object, true)); + assertType('array', class_uses($objectOrClassString, true)); + assertType('array|false', class_uses($objectOrString, true)); + assertType('array', class_uses($classString, true)); + assertType('array|false', class_uses($className, true)); assertType('false', class_uses('thisIsNotAClass', true)); - assertType('array', class_uses($object, false)); - assertType('array|false', class_uses($objectOrClassString, false)); - assertType('array|false', class_uses($objectOrString, false)); - assertType('array|false', class_uses($classString, false)); - assertType('array|false', class_uses($className, false)); + assertType('array', class_uses($object, false)); + assertType('array|false', class_uses($objectOrClassString, false)); + assertType('array|false', class_uses($objectOrString, false)); + assertType('array|false', class_uses($classString, false)); + assertType('array|false', class_uses($className, false)); assertType('false', class_uses('thisIsNotAClass', false)); - assertType('array', class_uses($object, $bool)); - assertType('array|false', class_uses($objectOrClassString, $bool)); - assertType('array|false', class_uses($objectOrString, $bool)); - assertType('array|false', class_uses($classString, $bool)); - assertType('array|false', class_uses($className, $bool)); + assertType('array', class_uses($object, $bool)); + assertType('array|false', class_uses($objectOrClassString, $bool)); + assertType('array|false', class_uses($objectOrString, $bool)); + assertType('array|false', class_uses($classString, $bool)); + assertType('array|false', class_uses($className, $bool)); assertType('false', class_uses('thisIsNotAClass', $bool)); - assertType('array', class_uses($object, $mixed)); - assertType('array|false', class_uses($objectOrClassString, $mixed)); - assertType('array|false', class_uses($objectOrString, $mixed)); - assertType('array|false', class_uses($classString, $mixed)); - assertType('array|false', class_uses($className, $mixed)); + assertType('array', class_uses($object, $mixed)); + assertType('array|false', class_uses($objectOrClassString, $mixed)); + assertType('array|false', class_uses($objectOrString, $mixed)); + assertType('array|false', class_uses($classString, $mixed)); + assertType('array|false', class_uses($className, $mixed)); assertType('false', class_uses('thisIsNotAClass', $mixed)); assertType('array', class_parents($object)); From 9de87a913585324dcf59ffd1754f6f51a100bab5 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 11 Jan 2023 10:35:13 +0100 Subject: [PATCH 09/10] Fix --- .../Analyser/data/class-implements.php | 100 +++++++++--------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/tests/PHPStan/Analyser/data/class-implements.php b/tests/PHPStan/Analyser/data/class-implements.php index a529e4e206..76689ec8cd 100644 --- a/tests/PHPStan/Analyser/data/class-implements.php +++ b/tests/PHPStan/Analyser/data/class-implements.php @@ -19,74 +19,74 @@ public function test( bool $bool, mixed $mixed, ): void { - assertType('array', class_implements($object)); - assertType('array', class_implements($objectOrClassString)); - assertType('array|false', class_implements($objectOrString)); - assertType('array', class_implements($classString)); - assertType('array|false', class_implements($className)); + assertType('array', class_implements($object)); + assertType('array', class_implements($objectOrClassString)); + assertType('array|false', class_implements($objectOrString)); + assertType('array', class_implements($classString)); + assertType('array|false', class_implements($className)); assertType('false', class_implements('thisIsNotAClass')); - assertType('array', class_implements($object, true)); - assertType('array', class_implements($objectOrClassString, true)); - assertType('array|false', class_implements($objectOrString, true)); - assertType('array', class_implements($classString, true)); - assertType('array|false', class_implements($className, true)); + assertType('array', class_implements($object, true)); + assertType('array', class_implements($objectOrClassString, true)); + assertType('array|false', class_implements($objectOrString, true)); + assertType('array', class_implements($classString, true)); + assertType('array|false', class_implements($className, true)); assertType('false', class_implements('thisIsNotAClass', true)); - assertType('array', class_implements($object, false)); - assertType('array|false', class_implements($objectOrClassString, false)); - assertType('array|false', class_implements($objectOrString, false)); - assertType('array|false', class_implements($classString, false)); - assertType('array|false', class_implements($className, false)); + assertType('array', class_implements($object, false)); + assertType('array|false', class_implements($objectOrClassString, false)); + assertType('array|false', class_implements($objectOrString, false)); + assertType('array|false', class_implements($classString, false)); + assertType('array|false', class_implements($className, false)); assertType('false', class_implements('thisIsNotAClass', false)); - assertType('array', class_implements($object, $bool)); - assertType('array|false', class_implements($objectOrClassString, $bool)); - assertType('array|false', class_implements($objectOrString, $bool)); - assertType('array|false', class_implements($classString, $bool)); - assertType('array|false', class_implements($className, $bool)); + assertType('array', class_implements($object, $bool)); + assertType('array|false', class_implements($objectOrClassString, $bool)); + assertType('array|false', class_implements($objectOrString, $bool)); + assertType('array|false', class_implements($classString, $bool)); + assertType('array|false', class_implements($className, $bool)); assertType('false', class_implements('thisIsNotAClass', $bool)); - assertType('array', class_implements($object, $mixed)); - assertType('array|false', class_implements($objectOrClassString, $mixed)); - assertType('array|false', class_implements($objectOrString, $mixed)); - assertType('array|false', class_implements($classString, $mixed)); - assertType('array|false', class_implements($className, $mixed)); + assertType('array', class_implements($object, $mixed)); + assertType('array|false', class_implements($objectOrClassString, $mixed)); + assertType('array|false', class_implements($objectOrString, $mixed)); + assertType('array|false', class_implements($classString, $mixed)); + assertType('array|false', class_implements($className, $mixed)); assertType('false', class_implements('thisIsNotAClass', $mixed)); - assertType('array', class_uses($object)); - assertType('array', class_uses($objectOrClassString)); - assertType('array|false', class_uses($objectOrString)); - assertType('array', class_uses($classString)); - assertType('array|false', class_uses($className)); + assertType('array', class_uses($object)); + assertType('array', class_uses($objectOrClassString)); + assertType('array|false', class_uses($objectOrString)); + assertType('array', class_uses($classString)); + assertType('array|false', class_uses($className)); assertType('false', class_uses('thisIsNotAClass')); - assertType('array', class_uses($object, true)); - assertType('array', class_uses($objectOrClassString, true)); - assertType('array|false', class_uses($objectOrString, true)); - assertType('array', class_uses($classString, true)); - assertType('array|false', class_uses($className, true)); + assertType('array', class_uses($object, true)); + assertType('array', class_uses($objectOrClassString, true)); + assertType('array|false', class_uses($objectOrString, true)); + assertType('array', class_uses($classString, true)); + assertType('array|false', class_uses($className, true)); assertType('false', class_uses('thisIsNotAClass', true)); - assertType('array', class_uses($object, false)); - assertType('array|false', class_uses($objectOrClassString, false)); - assertType('array|false', class_uses($objectOrString, false)); - assertType('array|false', class_uses($classString, false)); - assertType('array|false', class_uses($className, false)); + assertType('array', class_uses($object, false)); + assertType('array|false', class_uses($objectOrClassString, false)); + assertType('array|false', class_uses($objectOrString, false)); + assertType('array|false', class_uses($classString, false)); + assertType('array|false', class_uses($className, false)); assertType('false', class_uses('thisIsNotAClass', false)); - assertType('array', class_uses($object, $bool)); - assertType('array|false', class_uses($objectOrClassString, $bool)); - assertType('array|false', class_uses($objectOrString, $bool)); - assertType('array|false', class_uses($classString, $bool)); - assertType('array|false', class_uses($className, $bool)); + assertType('array', class_uses($object, $bool)); + assertType('array|false', class_uses($objectOrClassString, $bool)); + assertType('array|false', class_uses($objectOrString, $bool)); + assertType('array|false', class_uses($classString, $bool)); + assertType('array|false', class_uses($className, $bool)); assertType('false', class_uses('thisIsNotAClass', $bool)); - assertType('array', class_uses($object, $mixed)); - assertType('array|false', class_uses($objectOrClassString, $mixed)); - assertType('array|false', class_uses($objectOrString, $mixed)); - assertType('array|false', class_uses($classString, $mixed)); - assertType('array|false', class_uses($className, $mixed)); + assertType('array', class_uses($object, $mixed)); + assertType('array|false', class_uses($objectOrClassString, $mixed)); + assertType('array|false', class_uses($objectOrString, $mixed)); + assertType('array|false', class_uses($classString, $mixed)); + assertType('array|false', class_uses($className, $mixed)); assertType('false', class_uses('thisIsNotAClass', $mixed)); assertType('array', class_parents($object)); From 0d249865a237a8dec601543f686cef3fab28acc6 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Wed, 11 Jan 2023 11:18:26 +0100 Subject: [PATCH 10/10] Add regression test --- .../Arrays/IterableInForeachRuleTest.php | 5 +++++ tests/PHPStan/Rules/Arrays/data/bug-4335.php | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/PHPStan/Rules/Arrays/data/bug-4335.php diff --git a/tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php b/tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php index 19ddcbd783..b6abc55a97 100644 --- a/tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/IterableInForeachRuleTest.php @@ -75,4 +75,9 @@ public function testBug6564(): void $this->analyse([__DIR__ . '/data/bug-6564.php'], []); } + public function testBug4335(): void + { + $this->analyse([__DIR__ . '/data/bug-4335.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Arrays/data/bug-4335.php b/tests/PHPStan/Rules/Arrays/data/bug-4335.php new file mode 100644 index 0000000000..0824514d15 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/bug-4335.php @@ -0,0 +1,19 @@ + $v) { + var_dump($k, $v); + } + foreach (class_parents($this) as $k => $v) { + var_dump($k, $v); + } + foreach (class_uses($this) as $k => $v) { + var_dump($k, $v); + } + } +}