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 1652ca4

Browse files
Indicates whether a variable is global
1 parent e0c4844 commit 1652ca4

File tree

7 files changed

+117
-6
lines changed

7 files changed

+117
-6
lines changed

‎src/Analyser/MutatingScope.php‎

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,8 @@ final class MutatingScope implements Scope
177177

178178
private const KEEP_VOID_ATTRIBUTE_NAME = 'keepVoid';
179179

180+
private const IS_GLOBAL_ATTRIBUTE_NAME = 'isGlobal';
181+
180182
/** @var Type[] */
181183
private array $resolvedTypes = [];
182184

@@ -584,10 +586,25 @@ public function afterOpenSslCall(string $openSslFunctionName): self
584586
);
585587
}
586588

589+
/** @api */
590+
public function isGlobalVariable(string $variableName): bool
591+
{
592+
if ($this->isSuperglobalVariable($variableName)) {
593+
return true;
594+
}
595+
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;
602+
}
603+
587604
/** @api */
588605
public function hasVariableType(string $variableName): TrinaryLogic
589606
{
590-
if ($this->isGlobalVariable($variableName)) {
607+
if ($this->isSuperglobalVariable($variableName)) {
591608
return TrinaryLogic::createYes();
592609
}
593610

@@ -628,7 +645,7 @@ public function getVariableType(string $variableName): Type
628645

629646
$varExprString = '$' . $variableName;
630647
if (!array_key_exists($varExprString, $this->expressionTypes)) {
631-
if ($this->isGlobalVariable($variableName)) {
648+
if ($this->isSuperglobalVariable($variableName)) {
632649
return new ArrayType(new BenevolentUnionType([new IntegerType(), new StringType()]), new MixedType(true));
633650
}
634651
return new MixedType();
@@ -679,7 +696,7 @@ public function getMaybeDefinedVariables(): array
679696
return $variables;
680697
}
681698

682-
private function isGlobalVariable(string $variableName): bool
699+
private function isSuperglobalVariable(string $variableName): bool
683700
{
684701
return in_array($variableName, self::SUPERGLOBAL_VARIABLES, true);
685702
}
@@ -4168,9 +4185,13 @@ public function isUndefinedExpressionAllowed(Expr $expr): bool
41684185
return array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions);
41694186
}
41704187

4171-
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty): self
4188+
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty, bool$isGlobal = false): self
41724189
{
41734190
$node = new Variable($variableName);
4191+
if ($isGlobal || $this->isGlobalVariable($variableName)) {
4192+
$node->setAttribute(self::IS_GLOBAL_ATTRIBUTE_NAME, true);
4193+
}
4194+
41744195
$scope = $this->assignExpression($node, $type, $nativeType);
41754196
if ($certainty->no()) {
41764197
throw new ShouldNotHappenException();
@@ -4945,7 +4966,7 @@ private function createConditionalExpressions(
49454966
private function mergeVariableHolders(array $ourVariableTypeHolders, array $theirVariableTypeHolders): array
49464967
{
49474968
$intersectedVariableTypeHolders = [];
4948-
$globalVariableCallback = fn (Node $node) => $node instanceof Variable && is_string($node->name) && $this->isGlobalVariable($node->name);
4969+
$globalVariableCallback = fn (Node $node) => $node instanceof Variable && is_string($node->name) && $this->isSuperglobalVariable($node->name);
49494970
$nodeFinder = new NodeFinder();
49504971
foreach ($ourVariableTypeHolders as $exprString => $variableTypeHolder) {
49514972
if (isset($theirVariableTypeHolders[$exprString])) {

‎src/Analyser/NodeScopeResolver.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1967,7 +1967,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
19671967
continue;
19681968
}
19691969

1970-
$scope = $scope->assignVariable($var->name, new MixedType(), new MixedType(), TrinaryLogic::createYes());
1970+
$scope = $scope->assignVariable($var->name, new MixedType(), new MixedType(), TrinaryLogic::createYes(), true);
19711971
$vars[] = $var->name;
19721972
}
19731973
$scope = $this->processVarAnnotation($scope, $vars, $stmt);

‎src/Analyser/Scope.php‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ public function getFunctionName(): ?string;
5757

5858
public function getParentScope(): ?self;
5959

60+
public function isGlobalVariable(string $variableName): bool;
61+
6062
public function hasVariableType(string $variableName): TrinaryLogic;
6163

6264
public function getVariableType(string $variableName): Type;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Analyser;
4+
5+
use PhpParser\Node;
6+
use PhpParser\Node\Stmt\Return_;
7+
use PHPStan\Testing\TypeInferenceTestCase;
8+
9+
class GlobalVariableTest extends TypeInferenceTestCase
10+
{
11+
12+
public function testGlobalVariableInScript(): void
13+
{
14+
self::processFile(__DIR__ . '/data/global-in-script.php', function (Node $node, Scope $scope): void {
15+
if (!($node instanceof Return_)) {
16+
return;
17+
}
18+
19+
$this->assertTrue($scope->isGlobalVariable('FOO'));
20+
$this->assertFalse($scope->isGlobalVariable('whatever'));
21+
});
22+
}
23+
24+
public function testGlobalVariableInFunction(): void
25+
{
26+
self::processFile(__DIR__ . '/data/global-in-function.php', function (Node $node, Scope $scope): void {
27+
if (!($node instanceof Return_)) {
28+
return;
29+
}
30+
31+
$this->assertFalse($scope->isGlobalVariable('BAR'));
32+
$this->assertTrue($scope->isGlobalVariable('CONFIG'));
33+
$this->assertFalse($scope->isGlobalVariable('localVar'));
34+
});
35+
}
36+
37+
public function testGlobalVariableInClassMethod(): void
38+
{
39+
self::processFile(__DIR__ . '/data/global-in-class-method.php', function (Node $node, Scope $scope): void {
40+
if (!($node instanceof Return_)) {
41+
return;
42+
}
43+
44+
$this->assertFalse($scope->isGlobalVariable('count'));
45+
$this->assertTrue($scope->isGlobalVariable('GLB_A'));
46+
$this->assertTrue($scope->isGlobalVariable('GLB_B'));
47+
$this->assertFalse($scope->isGlobalVariable('key'));
48+
$this->assertFalse($scope->isGlobalVariable('step'));
49+
});
50+
}
51+
52+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
class ClassForGlobalTest
4+
{
5+
6+
public function doSomething(int $count = 3): bool
7+
{
8+
global $GLB_A, $GLB_B;
9+
10+
foreach ([1, 2, 3] as $key => $step) {
11+
break;
12+
}
13+
14+
return false;
15+
}
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
global $BAR;
4+
5+
function globalTest(string $BAR): void
6+
{
7+
global $CONFIG;
8+
9+
$localVar = true;
10+
11+
return;
12+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
global $FOO;
4+
5+
$FOO = "bar";
6+
$whatever = 15;
7+
8+
return;

0 commit comments

Comments
(0)

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