Skip to content

Commit d2c30d1

Browse files
authored
Adds type-specifying extension for settype()
1 parent b026126 commit d2c30d1

File tree

4 files changed

+770
-0
lines changed

4 files changed

+770
-0
lines changed

conf/config.neon

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,11 @@ services:
15291529
tags:
15301530
- phpstan.broker.dynamicFunctionReturnTypeExtension
15311531

1532+
-
1533+
class: PHPStan\Type\Php\SetTypeFunctionTypeSpecifyingExtension
1534+
tags:
1535+
- phpstan.typeSpecifier.functionTypeSpecifyingExtension
1536+
15321537
-
15331538
class: PHPStan\Type\Php\StrCaseFunctionsReturnTypeExtension
15341539
tags:
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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\Analyser\SpecifiedTypes;
8+
use PHPStan\Analyser\TypeSpecifier;
9+
use PHPStan\Analyser\TypeSpecifierAwareExtension;
10+
use PHPStan\Analyser\TypeSpecifierContext;
11+
use PHPStan\Reflection\FunctionReflection;
12+
use PHPStan\Type\ErrorType;
13+
use PHPStan\Type\FunctionTypeSpecifyingExtension;
14+
use PHPStan\Type\NullType;
15+
use PHPStan\Type\ObjectType;
16+
use PHPStan\Type\TypeCombinator;
17+
use stdClass;
18+
use function count;
19+
use function strtolower;
20+
21+
class SetTypeFunctionTypeSpecifyingExtension implements FunctionTypeSpecifyingExtension, TypeSpecifierAwareExtension
22+
{
23+
24+
private TypeSpecifier $typeSpecifier;
25+
26+
public function isFunctionSupported(FunctionReflection $functionReflection, FuncCall $node, TypeSpecifierContext $context): bool
27+
{
28+
return strtolower($functionReflection->getName()) === 'settype'
29+
&& count($node->getArgs()) > 1
30+
&& $context->null();
31+
}
32+
33+
public function specifyTypes(FunctionReflection $functionReflection, FuncCall $node, Scope $scope, TypeSpecifierContext $context): SpecifiedTypes
34+
{
35+
$value = $node->getArgs()[0]->value;
36+
$valueType = $scope->getType($value);
37+
$castType = $scope->getType($node->getArgs()[1]->value);
38+
39+
$constantStrings = $castType->getConstantStrings();
40+
if (count($constantStrings) < 1) {
41+
return new SpecifiedTypes();
42+
}
43+
44+
$types = [];
45+
46+
foreach ($constantStrings as $constantString) {
47+
switch ($constantString->getValue()) {
48+
case 'bool':
49+
case 'boolean':
50+
$types[] = $valueType->toBoolean();
51+
break;
52+
case 'int':
53+
case 'integer':
54+
$types[] = $valueType->toInteger();
55+
break;
56+
case 'float':
57+
case 'double':
58+
$types[] = $valueType->toFloat();
59+
break;
60+
case 'string':
61+
$types[] = $valueType->toString();
62+
break;
63+
case 'array':
64+
$types[] = $valueType->toArray();
65+
break;
66+
case 'object':
67+
$types[] = new ObjectType(stdClass::class);
68+
break;
69+
case 'null':
70+
$types[] = new NullType();
71+
break;
72+
default:
73+
$types[] = new ErrorType();
74+
}
75+
}
76+
77+
return $this->typeSpecifier->create(
78+
$value,
79+
TypeCombinator::union(...$types),
80+
TypeSpecifierContext::createTruthy(),
81+
true,
82+
$scope,
83+
);
84+
}
85+
86+
public function setTypeSpecifier(TypeSpecifier $typeSpecifier): void
87+
{
88+
$this->typeSpecifier = $typeSpecifier;
89+
}
90+
91+
}

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1431,6 +1431,7 @@ public function dataFileAsserts(): iterable
14311431
yield from $this->gatherAssertTypes(__DIR__ . '/data/assert-inheritance.php');
14321432
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-9123.php');
14331433
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10037.php');
1434+
yield from $this->gatherAssertTypes(__DIR__ . '/data/set-type-type-specifying.php');
14341435
}
14351436

14361437
/**

0 commit comments

Comments
 (0)