Skip to content

Commit aa66b84

Browse files
staabmondrejmirtes
authored andcommitted
implement gettype() return type extension
fixed mixed type fixed default return another test-case
1 parent 27666b9 commit aa66b84

File tree

4 files changed

+150
-0
lines changed

4 files changed

+150
-0
lines changed

conf/config.neon

+5
Original file line numberDiff line numberDiff line change
@@ -1495,6 +1495,11 @@ services:
14951495
tags:
14961496
- phpstan.broker.dynamicFunctionReturnTypeExtension
14971497

1498+
-
1499+
class: PHPStan\Type\Php\GettypeFunctionReturnTypeExtension
1500+
tags:
1501+
- phpstan.broker.dynamicFunctionReturnTypeExtension
1502+
14981503
-
14991504
class: PHPStan\Type\Php\GettimeofdayDynamicFunctionReturnTypeExtension
15001505
tags:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Php;
4+
5+
use PhpParser\Node\Expr\FuncCall;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Reflection\FunctionReflection;
8+
use PHPStan\Type\Constant\ConstantStringType;
9+
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
10+
use PHPStan\Type\IntersectionType;
11+
use PHPStan\Type\ResourceType;
12+
use PHPStan\Type\Type;
13+
use PHPStan\Type\TypeCombinator;
14+
use PHPStan\Type\TypeTraverser;
15+
use PHPStan\Type\UnionType;
16+
use function count;
17+
18+
class GettypeFunctionReturnTypeExtension implements DynamicFunctionReturnTypeExtension
19+
{
20+
21+
public function isFunctionSupported(FunctionReflection $functionReflection): bool
22+
{
23+
return $functionReflection->getName() === 'gettype';
24+
}
25+
26+
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type
27+
{
28+
if (count($functionCall->getArgs()) < 1) {
29+
return null;
30+
}
31+
32+
$valueType = $scope->getType($functionCall->getArgs()[0]->value);
33+
34+
return TypeTraverser::map($valueType, static function (Type $valueType, callable $traverse): Type {
35+
if ($valueType instanceof UnionType || $valueType instanceof IntersectionType) {
36+
return $traverse($valueType);
37+
}
38+
39+
if ($valueType->isString()->yes()) {
40+
return new ConstantStringType('string');
41+
}
42+
if ($valueType->isArray()->yes()) {
43+
return new ConstantStringType('array');
44+
}
45+
46+
if ($valueType->isBoolean()->yes()) {
47+
return new ConstantStringType('boolean');
48+
}
49+
50+
$resource = new ResourceType();
51+
if ($resource->isSuperTypeOf($valueType)->yes()) {
52+
return new UnionType([
53+
new ConstantStringType('resource'),
54+
new ConstantStringType('resource (closed)'),
55+
]);
56+
}
57+
58+
if ($valueType->isInteger()->yes()) {
59+
return new ConstantStringType('integer');
60+
}
61+
62+
if ($valueType->isFloat()->yes()) {
63+
// for historical reasons "double" is returned in case of a float, and not simply "float"
64+
return new ConstantStringType('double');
65+
}
66+
67+
if ($valueType->isNull()->yes()) {
68+
return new ConstantStringType('NULL');
69+
}
70+
71+
if ($valueType->isObject()->yes()) {
72+
return new ConstantStringType('object');
73+
}
74+
75+
return TypeCombinator::union(
76+
new ConstantStringType('string'),
77+
new ConstantStringType('array'),
78+
new ConstantStringType('boolean'),
79+
new ConstantStringType('resource'),
80+
new ConstantStringType('resource (closed)'),
81+
new ConstantStringType('integer'),
82+
new ConstantStringType('double'),
83+
new ConstantStringType('NULL'),
84+
new ConstantStringType('object'),
85+
new ConstantStringType('unknown type'),
86+
);
87+
});
88+
}
89+
90+
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -1268,6 +1268,7 @@ public function dataFileAsserts(): iterable
12681268
yield from $this->gatherAssertTypes(__DIR__ . '/data/globals.php');
12691269
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9208.php');
12701270
yield from $this->gatherAssertTypes(__DIR__ . '/data/finite-types.php');
1271+
yield from $this->gatherAssertTypes(__DIR__ . '/data/gettype.php');
12711272
}
12721273

12731274
/**
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
namespace GettypeFunction;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class A {}
8+
9+
/**
10+
* @param double $d
11+
* @param resource $r
12+
* @param int|string $intOrString
13+
* @param array|object $arrayOrObject
14+
*/
15+
function doFoo(bool $b, int $i, float $f, $d, $r, string $s, array $a, $mixed, $intOrString, $arrayOrObject) {
16+
$null = null;
17+
$resource = fopen('php://memory', 'r');
18+
$o = new \stdClass();
19+
$arrayObject = new \ArrayObject();
20+
$A = new A();
21+
22+
assertType("'boolean'", gettype($b));
23+
assertType("'boolean'", gettype(true));
24+
assertType("'boolean'", gettype(false));
25+
assertType("'integer'", gettype($i));
26+
assertType("'double'", gettype($f));
27+
assertType("'double'", gettype($d));
28+
assertType("'string'", gettype($s));
29+
assertType("'array'", gettype($a));
30+
assertType("'object'", gettype($o));
31+
assertType("'object'", gettype($arrayObject));
32+
assertType("'object'", gettype($A));
33+
// 'closed' was added in php 7.2
34+
assertType("'resource'|'resource (closed)'", gettype($r));
35+
assertType("'boolean'|'resource'|'resource (closed)'", gettype($resource));
36+
assertType("'NULL'", gettype($null));
37+
assertType("'array'|'boolean'|'double'|'integer'|'NULL'|'object'|'resource'|'resource (closed)'|'string'|'unknown type'", gettype($mixed));
38+
39+
assertType("'integer'|'string'", gettype($intOrString));
40+
assertType("'array'|'object'", gettype($arrayOrObject));
41+
}
42+
43+
/**
44+
* @param non-empty-string $nonEmptyString
45+
* @param non-falsy-string $falsyString
46+
* @param numeric-string $numericString
47+
* @param class-string $classString
48+
*/
49+
function strings($nonEmptyString, $falsyString, $numericString, $classString) {
50+
assertType("'string'", gettype($nonEmptyString));
51+
assertType("'string'", gettype($falsyString));
52+
assertType("'string'", gettype($numericString));
53+
assertType("'string'", gettype($classString));
54+
}

0 commit comments

Comments
 (0)