Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit db78deb

Browse files
Use a dedicated virtual node
1 parent 1652ca4 commit db78deb

9 files changed

+150
-15
lines changed

‎src/Analyser/MutatingScope.php‎

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use PHPStan\Node\Expr\GetIterableKeyTypeExpr;
3838
use PHPStan\Node\Expr\GetIterableValueTypeExpr;
3939
use PHPStan\Node\Expr\GetOffsetValueTypeExpr;
40+
use PHPStan\Node\Expr\GlobalVariableExpr;
4041
use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
4142
use PHPStan\Node\Expr\ParameterVariableOriginalValueExpr;
4243
use PHPStan\Node\Expr\PropertyInitializationExpr;
@@ -177,8 +178,6 @@ final class MutatingScope implements Scope
177178

178179
private const KEEP_VOID_ATTRIBUTE_NAME = 'keepVoid';
179180

180-
private const IS_GLOBAL_ATTRIBUTE_NAME = 'isGlobal';
181-
182181
/** @var Type[] */
183182
private array $resolvedTypes = [];
184183

@@ -593,12 +592,8 @@ public function isGlobalVariable(string $variableName): bool
593592
return true;
594593
}
595594

596-
$varExprString = '$' . $variableName;
597-
if (!isset($this->expressionTypes[$varExprString])) {
598-
return false;
599-
}
600-
601-
return $this->expressionTypes[$varExprString]->getExpr()->getAttribute(self::IS_GLOBAL_ATTRIBUTE_NAME) === true;
595+
$globalVariableExprString = $this->getNodeKey(new GlobalVariableExpr(new Variable($variableName)));
596+
return array_key_exists($globalVariableExprString, $this->expressionTypes);
602597
}
603598

604599
/** @api */
@@ -793,6 +788,10 @@ public function getType(Expr $node): Type
793788
return $propertyReflection->getReadableType();
794789
}
795790

791+
if ($node instanceof GlobalVariableExpr) {
792+
return $this->getType($node->getVar());
793+
}
794+
796795
$key = $this->getNodeKey($node);
797796

798797
if (!array_key_exists($key, $this->resolvedTypes)) {
@@ -4185,13 +4184,9 @@ public function isUndefinedExpressionAllowed(Expr $expr): bool
41854184
return array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions);
41864185
}
41874186

4188-
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty, bool$isGlobal = false): self
4187+
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty): self
41894188
{
41904189
$node = new Variable($variableName);
4191-
if ($isGlobal || $this->isGlobalVariable($variableName)) {
4192-
$node->setAttribute(self::IS_GLOBAL_ATTRIBUTE_NAME, true);
4193-
}
4194-
41954190
$scope = $this->assignExpression($node, $type, $nativeType);
41964191
if ($certainty->no()) {
41974192
throw new ShouldNotHappenException();
@@ -4294,6 +4289,17 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType,
42944289
}
42954290
}
42964291

4292+
if ($expr instanceof GlobalVariableExpr) {
4293+
foreach ($this->expressionTypeResolverExtensionRegistry->getExtensions() as $extension) {
4294+
$typeFromExtension = $extension->getType($expr, $this);
4295+
if ($typeFromExtension !== null) {
4296+
$type = $typeFromExtension;
4297+
break;
4298+
}
4299+
}
4300+
$scope = $scope->specifyExpressionType($expr->getVar(), $type, $nativeType, $certainty);
4301+
}
4302+
42974303
if ($certainty->no()) {
42984304
throw new ShouldNotHappenException();
42994305
}

‎src/Analyser/NodeScopeResolver.php‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
use PHPStan\Node\Expr\GetIterableKeyTypeExpr;
9090
use PHPStan\Node\Expr\GetIterableValueTypeExpr;
9191
use PHPStan\Node\Expr\GetOffsetValueTypeExpr;
92+
use PHPStan\Node\Expr\GlobalVariableExpr;
9293
use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
9394
use PHPStan\Node\Expr\PropertyInitializationExpr;
9495
use PHPStan\Node\Expr\SetExistingOffsetValueTypeExpr;
@@ -1967,7 +1968,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
19671968
continue;
19681969
}
19691970

1970-
$scope = $scope->assignVariable($var->name, new MixedType(), new MixedType(), TrinaryLogic::createYes(), true);
1971+
$scope = $scope->assignExpression(newGlobalVariableExpr($var), new MixedType(), new MixedType());
19711972
$vars[] = $var->name;
19721973
}
19731974
$scope = $this->processVarAnnotation($scope, $vars, $stmt);

‎src/Node/Expr/GlobalVariableExpr.php‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Node\Expr;
4+
5+
use Override;
6+
use PhpParser\Node\Expr;
7+
use PHPStan\Node\VirtualNode;
8+
9+
final class GlobalVariableExpr extends Expr implements VirtualNode
10+
{
11+
12+
public function __construct(private Expr $var)
13+
{
14+
parent::__construct([]);
15+
}
16+
17+
public function getVar(): Expr
18+
{
19+
return $this->var;
20+
}
21+
22+
#[Override]
23+
public function getType(): string
24+
{
25+
return 'PHPStan_Node_GlobalVariableExpr';
26+
}
27+
28+
/**
29+
* @return string[]
30+
*/
31+
#[Override]
32+
public function getSubNodeNames(): array
33+
{
34+
return [];
35+
}
36+
37+
}

‎src/Node/Printer/Printer.php‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PHPStan\Node\Expr\GetIterableKeyTypeExpr;
1010
use PHPStan\Node\Expr\GetIterableValueTypeExpr;
1111
use PHPStan\Node\Expr\GetOffsetValueTypeExpr;
12+
use PHPStan\Node\Expr\GlobalVariableExpr;
1213
use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
1314
use PHPStan\Node\Expr\ParameterVariableOriginalValueExpr;
1415
use PHPStan\Node\Expr\PropertyInitializationExpr;
@@ -92,4 +93,9 @@ protected function pPHPStan_Node_IssetExpr(IssetExpr $expr): string // phpcs:ign
9293
return sprintf('__phpstanIssetExpr(%s)', $this->p($expr->getExpr()));
9394
}
9495

96+
protected function pPHPStan_Node_GlobalVariableExpr(GlobalVariableExpr $expr): string // phpcs:ignore
97+
{
98+
return sprintf('__phpstanGlobalVariable(%s)', $this->p($expr->getVar()));
99+
}
100+
95101
}

‎tests/PHPStan/Analyser/ExpressionTypeResolverExtensionTest.php‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ class ExpressionTypeResolverExtensionTest extends TypeInferenceTestCase
1010

1111
public static function dataFileAsserts(): iterable
1212
{
13-
yield from self::gatherAssertTypes(__DIR__ . '/data/expression-type-resolver-extension.php');
13+
yield from self::gatherAssertTypes(__DIR__ . '/data/expression-type-resolver-extension-method-call-returns-bool.php');
14+
yield from self::gatherAssertTypes(__DIR__ . '/data/expression-type-resolver-extension-global-statement.php');
1415
}
1516

1617
/**
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace ExpressionTypeResolverExtension;
4+
5+
use PHPStan\Analyser\Scope;
6+
use PHPStan\Type\ArrayType;
7+
use PHPStan\Type\BenevolentUnionType;
8+
use PHPStan\Type\BooleanType;
9+
use PHPStan\Type\ExpressionTypeResolverExtension;
10+
use PHPStan\Type\IntegerType;
11+
use PHPStan\Type\MixedType;
12+
use PHPStan\Type\StringType;
13+
use PHPStan\Type\Type;
14+
use PhpParser\Node\Expr;
15+
use PhpParser\Node\Expr\Variable;
16+
use PHPStan\Node\Expr\GlobalVariableExpr;
17+
18+
class GlobalExpressionTypeResolverExtension implements ExpressionTypeResolverExtension {
19+
20+
public function getType(Expr $expr, Scope $scope): ?Type
21+
{
22+
23+
if (!$expr instanceof GlobalVariableExpr) {
24+
return null;
25+
}
26+
27+
$variableName = $expr->getVar()->name;
28+
29+
if ($variableName === 'MY_GLOBAL_BOOL') {
30+
return new BooleanType();
31+
}
32+
33+
if ($variableName === 'MY_GLOBAL_INT') {
34+
return new IntegerType();
35+
}
36+
37+
if ($variableName === 'MY_GLOBAL_STR') {
38+
return new StringType();
39+
}
40+
41+
if ($variableName === 'MY_GLOBAL_ARRAY') {
42+
return new ArrayType(new BenevolentUnionType([new IntegerType(), new StringType()]), new MixedType(true));
43+
}
44+
45+
return null;
46+
}
47+
48+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
// test file for ExpressionTypeResolverExtensionTest
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
global $MY_GLOBAL_BOOL, $ANOTHER_GLOBAL;
8+
9+
assertType('bool', $MY_GLOBAL_BOOL);
10+
assertType('mixed', $MY_GLOBAL_INT); // not declared in the global statement = no type assigned
11+
assertType('mixed', $ANOTHER_GLOBAL);
12+
13+
$testFct = function ($MY_GLOBAL_BOOL) {
14+
/** @var float $MY_GLOBAL_STR */
15+
global $MY_GLOBAL_INT, $MY_GLOBAL_STR, $MY_GLOBAL_ARRAY;
16+
17+
$MY_GLOBAL_ARRAY = new ArrayIterator([1, 2, 3]);
18+
19+
assertType('mixed', $MY_GLOBAL_BOOL); // not declared in the global statement = no type assigned
20+
assertType('float', $MY_GLOBAL_STR); // overriden by PHPDoc
21+
assertType('ArrayIterator<int, int>', $MY_GLOBAL_ARRAY); // overriden by value assign expression
22+
assertType('int', $MY_GLOBAL_INT);
23+
};
24+
25+
$testClass = new class () {
26+
public function foo($MY_GLOBAL_INT) {
27+
global $MY_GLOBAL_STR;
28+
29+
assertType('string', $MY_GLOBAL_STR);
30+
assertType('mixed', $MY_GLOBAL_INT);
31+
}
32+
};

‎tests/PHPStan/Analyser/expression-type-resolver-extension.neon‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# config for ExpressionTypeResolverExtensionTest
22
services:
3+
-
4+
class: ExpressionTypeResolverExtension\GlobalExpressionTypeResolverExtension
5+
tags:
6+
- phpstan.broker.expressionTypeResolverExtension
37
-
48
class: ExpressionTypeResolverExtension\MethodCallReturnsBoolExpressionTypeResolverExtension
59
tags:

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /