-
Notifications
You must be signed in to change notification settings - Fork 479
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DynamicFunctionReturnTypeExtension for the
get_debug_type
function.
Conditional type specifier for `get_debug_type` function.
- Loading branch information
1 parent
1ebcae0
commit 57b6434
Showing
8 changed files
with
192 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Type\Php; | ||
|
||
use Closure; | ||
use PhpParser\Node\Expr\FuncCall; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Reflection\FunctionReflection; | ||
use PHPStan\Type\Constant\ConstantStringType; | ||
use PHPStan\Type\DynamicFunctionReturnTypeExtension; | ||
use PHPStan\Type\StringType; | ||
use PHPStan\Type\Type; | ||
use PHPStan\Type\UnionType; | ||
use function array_map; | ||
use function count; | ||
|
||
class GetDebugTypeFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension | ||
{ | ||
|
||
public function isFunctionSupported(FunctionReflection $functionReflection): bool | ||
{ | ||
return $functionReflection->getName() === 'get_debug_type'; | ||
} | ||
|
||
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type | ||
{ | ||
if (count($functionCall->getArgs()) < 1) { | ||
return null; | ||
} | ||
|
||
$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(); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
<?php | ||
|
||
namespace GetDebugType; | ||
|
||
use function PHPStan\Testing\assertType; | ||
|
||
final class A {} | ||
|
||
/** | ||
* @param double $d | ||
* @param resource $r | ||
* @param int|string $intOrString | ||
* @param array|A $arrayOrObject | ||
*/ | ||
function doFoo(bool $b, int $i, float $f, $d, $r, string $s, array $a, $intOrString, $arrayOrObject) { | ||
$null = null; | ||
$resource = fopen('php://memory', 'r'); | ||
$o = new \stdClass(); | ||
$A = new A(); | ||
$anonymous = new class {}; | ||
|
||
assertType("'bool'", get_debug_type($b)); | ||
assertType("'bool'", get_debug_type(true)); | ||
assertType("'bool'", get_debug_type(false)); | ||
assertType("'int'", get_debug_type($i)); | ||
assertType("'float'", get_debug_type($f)); | ||
assertType("'float'", get_debug_type($d)); | ||
assertType("'string'", get_debug_type($s)); | ||
assertType("'array'", get_debug_type($a)); | ||
assertType("string", get_debug_type($o)); | ||
assertType("'GetDebugType\\\\A'", get_debug_type($A)); | ||
assertType("string", get_debug_type($r)); | ||
assertType("'bool'|string", get_debug_type($resource)); | ||
assertType("'null'", get_debug_type($null)); | ||
assertType("'int'|'string'", get_debug_type($intOrString)); | ||
assertType("'array'|'GetDebugType\\\\A'", get_debug_type($arrayOrObject)); | ||
assertType("'class@anonymous'", get_debug_type($anonymous)); | ||
} | ||
|
||
/** | ||
* @param non-empty-string $nonEmptyString | ||
* @param non-falsy-string $falsyString | ||
* @param numeric-string $numericString | ||
* @param class-string $classString | ||
*/ | ||
function strings($nonEmptyString, $falsyString, $numericString, $classString) { | ||
assertType("'string'", get_debug_type($nonEmptyString)); | ||
assertType("'string'", get_debug_type($falsyString)); | ||
assertType("'string'", get_debug_type($numericString)); | ||
assertType("'string'", get_debug_type($classString)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters