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 72730d1

Browse files
Use a dedicated virtual node
1 parent bedb4d1 commit 72730d1

9 files changed

+149
-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\NativeTypeExpr;
4142
use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
4243
use PHPStan\Node\Expr\ParameterVariableOriginalValueExpr;
@@ -179,8 +180,6 @@ final class MutatingScope implements Scope
179180

180181
private const KEEP_VOID_ATTRIBUTE_NAME = 'keepVoid';
181182

182-
private const IS_GLOBAL_ATTRIBUTE_NAME = 'isGlobal';
183-
184183
/** @var Type[] */
185184
private array $resolvedTypes = [];
186185

@@ -618,12 +617,8 @@ public function isGlobalVariable(string $variableName): bool
618617
return true;
619618
}
620619

621-
$varExprString = '$' . $variableName;
622-
if (!isset($this->expressionTypes[$varExprString])) {
623-
return false;
624-
}
625-
626-
return $this->expressionTypes[$varExprString]->getExpr()->getAttribute(self::IS_GLOBAL_ATTRIBUTE_NAME) === true;
620+
$globalVariableExprString = $this->getNodeKey(new GlobalVariableExpr(new Variable($variableName)));
621+
return array_key_exists($globalVariableExprString, $this->expressionTypes);
627622
}
628623

629624
/** @api */
@@ -824,6 +819,10 @@ public function getType(Expr $node): Type
824819
return $propertyReflection->getReadableType();
825820
}
826821

822+
if ($node instanceof GlobalVariableExpr) {
823+
return $this->getType($node->getVar());
824+
}
825+
827826
$key = $this->getNodeKey($node);
828827

829828
if (!array_key_exists($key, $this->resolvedTypes)) {
@@ -4221,13 +4220,9 @@ public function isUndefinedExpressionAllowed(Expr $expr): bool
42214220
return array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions);
42224221
}
42234222

4224-
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty, bool$isGlobal = false): self
4223+
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty): self
42254224
{
42264225
$node = new Variable($variableName);
4227-
if ($isGlobal || $this->isGlobalVariable($variableName)) {
4228-
$node->setAttribute(self::IS_GLOBAL_ATTRIBUTE_NAME, true);
4229-
}
4230-
42314226
$scope = $this->assignExpression($node, $type, $nativeType);
42324227
if ($certainty->no()) {
42334228
throw new ShouldNotHappenException();
@@ -4330,6 +4325,17 @@ public function specifyExpressionType(Expr $expr, Type $type, Type $nativeType,
43304325
}
43314326
}
43324327

4328+
if ($expr instanceof GlobalVariableExpr) {
4329+
foreach ($this->expressionTypeResolverExtensionRegistry->getExtensions() as $extension) {
4330+
$typeFromExtension = $extension->getType($expr, $this);
4331+
if ($typeFromExtension !== null) {
4332+
$type = $typeFromExtension;
4333+
break;
4334+
}
4335+
}
4336+
$scope = $scope->specifyExpressionType($expr->getVar(), $type, $nativeType, $certainty);
4337+
}
4338+
43334339
if ($certainty->no()) {
43344340
throw new ShouldNotHappenException();
43354341
}

‎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\NativeTypeExpr;
9394
use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
9495
use PHPStan\Node\Expr\PropertyInitializationExpr;
@@ -1976,7 +1977,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
19761977
continue;
19771978
}
19781979

1979-
$scope = $scope->assignVariable($var->name, new MixedType(), new MixedType(), TrinaryLogic::createYes(), true);
1980+
$scope = $scope->assignExpression(newGlobalVariableExpr($var), new MixedType(), new MixedType());
19801981
$vars[] = $var->name;
19811982
}
19821983
$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\NativeTypeExpr;
1314
use PHPStan\Node\Expr\OriginalPropertyTypeExpr;
1415
use PHPStan\Node\Expr\ParameterVariableOriginalValueExpr;
@@ -98,4 +99,9 @@ protected function pPHPStan_Node_IssetExpr(IssetExpr $expr): string // phpcs:ign
9899
return sprintf('__phpstanIssetExpr(%s)', $this->p($expr->getExpr()));
99100
}
100101

102+
protected function pPHPStan_Node_GlobalVariableExpr(GlobalVariableExpr $expr): string // phpcs:ignore
103+
{
104+
return sprintf('__phpstanGlobalVariable(%s)', $this->p($expr->getVar()));
105+
}
106+
101107
}

‎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: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
namespace ExpressionTypeResolverExtension;
4+
5+
use PHPStan\Analyser\Scope;
6+
use PHPStan\Node\Expr\GlobalVariableExpr;
7+
use PHPStan\Type\ArrayType;
8+
use PHPStan\Type\BenevolentUnionType;
9+
use PHPStan\Type\BooleanType;
10+
use PHPStan\Type\ExpressionTypeResolverExtension;
11+
use PHPStan\Type\IntegerType;
12+
use PHPStan\Type\MixedType;
13+
use PHPStan\Type\StringType;
14+
use PHPStan\Type\Type;
15+
use PhpParser\Node\Expr;
16+
17+
class GlobalExpressionTypeResolverExtension implements ExpressionTypeResolverExtension {
18+
19+
public function getType(Expr $expr, Scope $scope): ?Type
20+
{
21+
22+
if (!$expr instanceof GlobalVariableExpr) {
23+
return null;
24+
}
25+
26+
$variableName = $expr->getVar()->name;
27+
28+
if ($variableName === 'MY_GLOBAL_BOOL') {
29+
return new BooleanType();
30+
}
31+
32+
if ($variableName === 'MY_GLOBAL_INT') {
33+
return new IntegerType();
34+
}
35+
36+
if ($variableName === 'MY_GLOBAL_STR') {
37+
return new StringType();
38+
}
39+
40+
if ($variableName === 'MY_GLOBAL_ARRAY') {
41+
return new ArrayType(new BenevolentUnionType([new IntegerType(), new StringType()]), new MixedType(true));
42+
}
43+
44+
return null;
45+
}
46+
47+
}
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 によって変換されたページ (->オリジナル) /