From 1e360f4f8a304804546832de5e796e160eba36c5 Mon Sep 17 00:00:00 2001 From: patrickkusebauch Date: Sat, 10 Feb 2024 22:33:45 +0100 Subject: [PATCH] DynamicFunctionReturnTypeExtension for the get_debug_type function. --- conf/config.neon | 5 ++ ...etDebugTypeFunctionReturnTypeExtension.php | 89 +++++++++++++++++++ .../Analyser/NodeScopeResolverTest.php | 4 + .../PHPStan/Analyser/data/get-debug-type.php | 51 +++++++++++ 4 files changed, 149 insertions(+) create mode 100644 src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php create mode 100644 tests/PHPStan/Analyser/data/get-debug-type.php diff --git a/conf/config.neon b/conf/config.neon index 5baed2912bc..d2082ff47af 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -1360,6 +1360,11 @@ services: tags: - phpstan.broker.dynamicFunctionReturnTypeExtension + - + class: PHPStan\Type\Php\GetDebugTypeFunctionReturnTypeExtension + tags: + - phpstan.broker.dynamicFunctionReturnTypeExtension + - class: PHPStan\Type\Php\GetParentClassDynamicFunctionReturnTypeExtension tags: diff --git a/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php b/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php new file mode 100644 index 00000000000..8cbafd7c6b2 --- /dev/null +++ b/src/Type/Php/GetDebugTypeFunctionReturnTypeExtension.php @@ -0,0 +1,89 @@ +getName() === 'get_debug_type'; + } + + public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type + { + $argType = $scope->getType($functionCall->getArgs()[0]->value); + if ($argType instanceof UnionType) { + return new UnionType(array_map(Closure::fromCallable([self::class, 'resolveOneType']), $argType->getTypes())); + } + return self::resolveOneType($argType); + } + + /** + * @see https://www.php.net/manual/en/function.get-debug-type.php#refsect1-function.get-debug-type-returnvalues + */ + private static function resolveOneType(Type $type): Type + { + if ($type->isNull()->yes()) { + return new ConstantStringType('null'); + } + if ($type->isBoolean()->yes()) { + return new ConstantStringType('bool'); + } + if ($type->isInteger()->yes()) { + return new ConstantStringType('int'); + } + if ($type->isFloat()->yes()) { + return new ConstantStringType('float'); + } + if ($type->isString()->yes()) { + return new ConstantStringType('string'); + } + if ($type->isArray()->yes()) { + return new ConstantStringType('array'); + } + + // "resources" type+state is skipped since we cannot infer the state + + if ($type->isObject()->yes()) { + $classNames = $type->getObjectClassNames(); + $reflections = $type->getObjectClassReflections(); + + $types = []; + foreach ($classNames as $index => $className) { + // if the class is not final, the actual returned string might be of a child class + if ($reflections[$index]->isFinal() && !$reflections[$index]->isAnonymous()) { + $types[] = new ConstantStringType($className); + } + + if ($reflections[$index]->isAnonymous()) { // phpcs:ignore + $types[] = new ConstantStringType('class@anonymous'); + } + } + + switch (count($types)) { + case 0: + return new StringType(); + case 1: + return $types[0]; + default: + return new UnionType($types); + } + } + + return new StringType(); + } + +} diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 11b860600c2..4e860330cb2 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1385,6 +1385,10 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/call-user-func-php7.php'); } + if (PHP_VERSION_ID >= 80000) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/get-debug-type.php'); + } + yield from $this->gatherAssertTypes(__DIR__ . '/data/gettype.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/array_splice.php'); yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-9542.php'); diff --git a/tests/PHPStan/Analyser/data/get-debug-type.php b/tests/PHPStan/Analyser/data/get-debug-type.php new file mode 100644 index 00000000000..975ea626139 --- /dev/null +++ b/tests/PHPStan/Analyser/data/get-debug-type.php @@ -0,0 +1,51 @@ +