diff --git a/conf/config.neon b/conf/config.neon index 38a1518a30..4e3d04027e 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -1497,6 +1497,11 @@ services: tags: - phpstan.broker.dynamicFunctionReturnTypeExtension + - + class: PHPStan\Type\Php\HighlightStringDynamicReturnTypeExtension + tags: + - phpstan.broker.dynamicFunctionReturnTypeExtension + - class: PHPStan\Type\Php\IntdivThrowTypeExtension tags: diff --git a/src/Php/PhpVersion.php b/src/Php/PhpVersion.php index e08bad1432..ff3f1966e2 100644 --- a/src/Php/PhpVersion.php +++ b/src/Php/PhpVersion.php @@ -343,4 +343,9 @@ public function hasExitAsFunction(): bool return $this->versionId >= 80400; } + public function highlightStringDoesNotReturnFalse(): bool + { + return $this->versionId >= 80400; + } + } diff --git a/src/Type/Php/HighlightStringDynamicReturnTypeExtension.php b/src/Type/Php/HighlightStringDynamicReturnTypeExtension.php new file mode 100644 index 0000000000..12236c20cf --- /dev/null +++ b/src/Type/Php/HighlightStringDynamicReturnTypeExtension.php @@ -0,0 +1,51 @@ +getName() === 'highlight_string'; + } + + public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): Type + { + $args = $functionCall->getArgs(); + if (count($args) < 2) { + if ($this->phpVersion->highlightStringDoesNotReturnFalse()) { + return new ConstantBooleanType(true); + } + + return new BooleanType(); + } + + $returnType = $scope->getType($args[1]->value); + if ($returnType->isTrue()->yes()) { + return new StringType(); + } + + if ($this->phpVersion->highlightStringDoesNotReturnFalse()) { + return new ConstantBooleanType(true); + } + + return new BooleanType(); + } + +} diff --git a/stubs/core.stub b/stubs/core.stub index 2cb6f29448..e3a0b751ee 100644 --- a/stubs/core.stub +++ b/stubs/core.stub @@ -35,12 +35,6 @@ function highlight_file($var, bool $return = false) {} */ function show_source($var, bool $return = false) {} -/** - * @param mixed $var - * @return ($return is true ? string : bool) - */ -function highlight_string($var, bool $return = false) {} - /** * @param mixed $var * @param bool $return diff --git a/tests/PHPStan/Analyser/nsrt/pr-1244-php-84.php b/tests/PHPStan/Analyser/nsrt/pr-1244-php-84.php new file mode 100644 index 0000000000..17bf3d44c1 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/pr-1244-php-84.php @@ -0,0 +1,35 @@ += 8.4 + +namespace Pr1244Php84; + +use function PHPStan\Testing\assertType; + +function foo() { + /** @var string $string */ + $string = doFoo(); + + assertType('null', var_export()); + assertType('null', var_export($string)); + assertType('null', var_export($string, false)); + assertType('string', var_export($string, true)); + + assertType('true', highlight_string()); + assertType('true', highlight_string($string)); + assertType('true', highlight_string($string, false)); + assertType('string', highlight_string($string, true)); + + assertType('bool', highlight_file()); + assertType('bool', highlight_file($string)); + assertType('bool', highlight_file($string, false)); + assertType('string', highlight_file($string, true)); + + assertType('bool', show_source()); + assertType('bool', show_source($string)); + assertType('bool', show_source($string, false)); + assertType('string', show_source($string, true)); + + assertType('true', print_r()); + assertType('true', print_r($string)); + assertType('true', print_r($string, false)); + assertType('string', print_r($string, true)); +} diff --git a/tests/PHPStan/Analyser/nsrt/pr-1244.php b/tests/PHPStan/Analyser/nsrt/pr-1244.php index 6aec5018a0..a8c0fc19ae 100644 --- a/tests/PHPStan/Analyser/nsrt/pr-1244.php +++ b/tests/PHPStan/Analyser/nsrt/pr-1244.php @@ -1,4 +1,4 @@ -