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 bedb4d1

Browse files
Indicates whether a variable is global
1 parent 362f47a commit bedb4d1

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
@@ -179,6 +179,8 @@ final class MutatingScope implements Scope
179179

180180
private const KEEP_VOID_ATTRIBUTE_NAME = 'keepVoid';
181181

182+
private const IS_GLOBAL_ATTRIBUTE_NAME = 'isGlobal';
183+
182184
/** @var Type[] */
183185
private array $resolvedTypes = [];
184186

@@ -609,10 +611,25 @@ public function afterOpenSslCall(string $openSslFunctionName): self
609611
);
610612
}
611613

614+
/** @api */
615+
public function isGlobalVariable(string $variableName): bool
616+
{
617+
if ($this->isSuperglobalVariable($variableName)) {
618+
return true;
619+
}
620+
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;
627+
}
628+
612629
/** @api */
613630
public function hasVariableType(string $variableName): TrinaryLogic
614631
{
615-
if ($this->isGlobalVariable($variableName)) {
632+
if ($this->isSuperglobalVariable($variableName)) {
616633
return TrinaryLogic::createYes();
617634
}
618635

@@ -653,7 +670,7 @@ public function getVariableType(string $variableName): Type
653670

654671
$varExprString = '$' . $variableName;
655672
if (!array_key_exists($varExprString, $this->expressionTypes)) {
656-
if ($this->isGlobalVariable($variableName)) {
673+
if ($this->isSuperglobalVariable($variableName)) {
657674
return new ArrayType(new BenevolentUnionType([new IntegerType(), new StringType()]), new MixedType(true));
658675
}
659676
return new MixedType();
@@ -704,7 +721,7 @@ public function getMaybeDefinedVariables(): array
704721
return $variables;
705722
}
706723

707-
private function isGlobalVariable(string $variableName): bool
724+
private function isSuperglobalVariable(string $variableName): bool
708725
{
709726
return in_array($variableName, self::SUPERGLOBAL_VARIABLES, true);
710727
}
@@ -4204,9 +4221,13 @@ public function isUndefinedExpressionAllowed(Expr $expr): bool
42044221
return array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions);
42054222
}
42064223

4207-
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty): self
4224+
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty, bool$isGlobal = false): self
42084225
{
42094226
$node = new Variable($variableName);
4227+
if ($isGlobal || $this->isGlobalVariable($variableName)) {
4228+
$node->setAttribute(self::IS_GLOBAL_ATTRIBUTE_NAME, true);
4229+
}
4230+
42104231
$scope = $this->assignExpression($node, $type, $nativeType);
42114232
if ($certainty->no()) {
42124233
throw new ShouldNotHappenException();
@@ -4979,7 +5000,7 @@ private function createConditionalExpressions(
49795000
private function mergeVariableHolders(array $ourVariableTypeHolders, array $theirVariableTypeHolders): array
49805001
{
49815002
$intersectedVariableTypeHolders = [];
4982-
$globalVariableCallback = fn (Node $node) => $node instanceof Variable && is_string($node->name) && $this->isGlobalVariable($node->name);
5003+
$globalVariableCallback = fn (Node $node) => $node instanceof Variable && is_string($node->name) && $this->isSuperglobalVariable($node->name);
49835004
$nodeFinder = new NodeFinder();
49845005
foreach ($ourVariableTypeHolders as $exprString => $variableTypeHolder) {
49855006
if (isset($theirVariableTypeHolders[$exprString])) {

‎src/Analyser/NodeScopeResolver.php‎

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

1979-
$scope = $scope->assignVariable($var->name, new MixedType(), new MixedType(), TrinaryLogic::createYes());
1979+
$scope = $scope->assignVariable($var->name, new MixedType(), new MixedType(), TrinaryLogic::createYes(), true);
19801980
$vars[] = $var->name;
19811981
}
19821982
$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 によって変換されたページ (->オリジナル) /