Skip to content

Commit ce9f61d

Browse files
committed
Avoid polluting the scope by adding superglobal expressions lazily
1 parent e05ff36 commit ce9f61d

File tree

5 files changed

+31
-90
lines changed

5 files changed

+31
-90
lines changed

Diff for: src/Analyser/MutatingScope.php

+18-2
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,10 @@ public function afterOpenSslCall(string $openSslFunctionName): self
513513
/** @api */
514514
public function hasVariableType(string $variableName): TrinaryLogic
515515
{
516+
if ($this->isGlobalVariable($variableName)) {
517+
return TrinaryLogic::createYes();
518+
}
519+
516520
$varExprString = '$' . $variableName;
517521
if (!isset($this->expressionTypes[$varExprString])) {
518522
if ($this->canAnyVariableExist()) {
@@ -544,13 +548,20 @@ public function getVariableType(string $variableName): Type
544548
}
545549
}
546550

551+
$varExprString = '$' . $variableName;
552+
547553
if ($this->hasVariableType($variableName)->no()) {
548554
throw new UndefinedVariableException($this, $variableName);
549555
}
550556

551-
$varExprString = '$' . $variableName;
552557
if (!array_key_exists($varExprString, $this->expressionTypes)) {
553-
return new MixedType();
558+
if (!$this->isGlobalVariable($variableName)) {
559+
return new MixedType();
560+
}
561+
562+
$superglobalType = new ArrayType(new BenevolentUnionType([new IntegerType(), new StringType()]), new MixedType(true));
563+
$this->expressionTypes[$varExprString] = ExpressionTypeHolder::createYes(new Variable($variableName), $superglobalType);
564+
$this->nativeExpressionTypes[$varExprString] = ExpressionTypeHolder::createYes(new Variable($variableName), $superglobalType);
554565
}
555566

556567
return TypeUtils::resolveLateResolvableTypes($this->expressionTypes[$varExprString]->getType());
@@ -598,6 +609,11 @@ public function getMaybeDefinedVariables(): array
598609
return $variables;
599610
}
600611

612+
private function isGlobalVariable(string $variableName): bool
613+
{
614+
return in_array($variableName, self::SUPERGLOBAL_VARIABLES, true);
615+
}
616+
601617
/** @api */
602618
public function hasConstant(Name $name): bool
603619
{

Diff for: src/Analyser/ScopeFactory.php

+1-21
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@
22

33
namespace PHPStan\Analyser;
44

5-
use PhpParser\Node\Expr\Variable;
6-
use PHPStan\Type\ArrayType;
7-
use PHPStan\Type\BenevolentUnionType;
8-
use PHPStan\Type\IntegerType;
9-
use PHPStan\Type\MixedType;
10-
use PHPStan\Type\StringType;
11-
125
/**
136
* @api
147
*/
@@ -21,20 +14,7 @@ public function __construct(private InternalScopeFactory $internalScopeFactory)
2114

2215
public function create(ScopeContext $context): MutatingScope
2316
{
24-
$superglobalType = new ArrayType(new BenevolentUnionType([new IntegerType(), new StringType()]), new MixedType(true));
25-
$expressionTypes = [
26-
'$GLOBALS' => ExpressionTypeHolder::createYes(new Variable('GLOBALS'), $superglobalType),
27-
'$_SERVER' => ExpressionTypeHolder::createYes(new Variable('_SERVER'), $superglobalType),
28-
'$_GET' => ExpressionTypeHolder::createYes(new Variable('_GET'), $superglobalType),
29-
'$_POST' => ExpressionTypeHolder::createYes(new Variable('_POST'), $superglobalType),
30-
'$_FILES' => ExpressionTypeHolder::createYes(new Variable('_FILES'), $superglobalType),
31-
'$_COOKIE' => ExpressionTypeHolder::createYes(new Variable('_COOKIE'), $superglobalType),
32-
'$_SESSION' => ExpressionTypeHolder::createYes(new Variable('_SESSION'), $superglobalType),
33-
'$_REQUEST' => ExpressionTypeHolder::createYes(new Variable('_REQUEST'), $superglobalType),
34-
'$_ENV' => ExpressionTypeHolder::createYes(new Variable('_ENV'), $superglobalType),
35-
];
36-
37-
return $this->internalScopeFactory->create($context, false, null, null, $expressionTypes, $expressionTypes);
17+
return $this->internalScopeFactory->create($context);
3818
}
3919

4020
}

Diff for: tests/PHPStan/Analyser/ScopeTest.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ public function testDefinedVariables(): void
261261
->assignVariable('a', new ConstantStringType('a'), new StringType(), TrinaryLogic::createYes())
262262
->assignVariable('b', new ConstantStringType('b'), new StringType(), TrinaryLogic::createMaybe());
263263

264-
$this->assertSame(['GLOBALS', '_SERVER', '_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_REQUEST', '_ENV', 'a'], $scope->getDefinedVariables());
264+
$this->assertSame(['a'], $scope->getDefinedVariables());
265265
}
266266

267267
public function testMaybeDefinedVariables(): void

Diff for: tests/PHPStan/Analyser/nsrt/get-defined-vars.php

+10-10
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,37 @@
1010

1111
function doFoo(int $param) {
1212
$local = "foo";
13-
assertType('array{GLOBALS: array<mixed>, _SERVER: array<mixed>, _GET: array<mixed>, _POST: array<mixed>, _FILES: array<mixed>, _COOKIE: array<mixed>, _SESSION: array<mixed>, _REQUEST: array<mixed>, _ENV: array<mixed>, param: int, local: \'foo\'}', get_defined_vars());
14-
assertType('array{\'GLOBALS\', \'_SERVER\', \'_GET\', \'_POST\', \'_FILES\', \'_COOKIE\', \'_SESSION\', \'_REQUEST\', \'_ENV\', \'param\', \'local\'}', array_keys(get_defined_vars()));
13+
assertType('array{param: int, local: \'foo\'}', get_defined_vars());
14+
assertType('array{\'param\', \'local\'}', array_keys(get_defined_vars()));
1515
}
1616

1717
function doBar(int $param) {
1818
global $global;
1919
$local = "foo";
20-
assertType('array{GLOBALS: array<mixed>, _SERVER: array<mixed>, _GET: array<mixed>, _POST: array<mixed>, _FILES: array<mixed>, _COOKIE: array<mixed>, _SESSION: array<mixed>, _REQUEST: array<mixed>, _ENV: array<mixed>, param: int, global: mixed, local: \'foo\'}', get_defined_vars());
21-
assertType('array{\'GLOBALS\', \'_SERVER\', \'_GET\', \'_POST\', \'_FILES\', \'_COOKIE\', \'_SESSION\', \'_REQUEST\', \'_ENV\', \'param\', \'global\', \'local\'}', array_keys(get_defined_vars()));
20+
assertType('array{param: int, global: mixed, local: \'foo\'}', get_defined_vars());
21+
assertType('array{\'param\', \'global\', \'local\'}', array_keys(get_defined_vars()));
2222
}
2323

2424
function doConditional(int $param) {
2525
$local = "foo";
2626
if(true) {
2727
$conditional = "bar";
28-
assertType('array{GLOBALS: array<mixed>, _SERVER: array<mixed>, _GET: array<mixed>, _POST: array<mixed>, _FILES: array<mixed>, _COOKIE: array<mixed>, _SESSION: array<mixed>, _REQUEST: array<mixed>, _ENV: array<mixed>, param: int, local: \'foo\', conditional: \'bar\'}', get_defined_vars());
28+
assertType('array{param: int, local: \'foo\', conditional: \'bar\'}', get_defined_vars());
2929
} else {
3030
$other = "baz";
31-
assertType('array{GLOBALS: array<mixed>, _SERVER: array<mixed>, _GET: array<mixed>, _POST: array<mixed>, _FILES: array<mixed>, _COOKIE: array<mixed>, _SESSION: array<mixed>, _REQUEST: array<mixed>, _ENV: array<mixed>, param: int, local: \'foo\', other: \'baz\'}', get_defined_vars());
31+
assertType('array{param: int, local: \'foo\', other: \'baz\'}', get_defined_vars());
3232
}
33-
assertType('array{GLOBALS: array<mixed>, _SERVER: array<mixed>, _GET: array<mixed>, _POST: array<mixed>, _FILES: array<mixed>, _COOKIE: array<mixed>, _SESSION: array<mixed>, _REQUEST: array<mixed>, _ENV: array<mixed>, param: int, local: \'foo\', conditional: \'bar\'}', get_defined_vars());
33+
assertType('array{param: int, local: \'foo\', conditional: \'bar\'}', get_defined_vars());
3434
}
3535

3636
function doRandom(int $param) {
3737
$local = "foo";
3838
if(rand(0, 1)) {
3939
$random1 = "bar";
40-
assertType('array{GLOBALS: array<mixed>, _SERVER: array<mixed>, _GET: array<mixed>, _POST: array<mixed>, _FILES: array<mixed>, _COOKIE: array<mixed>, _SESSION: array<mixed>, _REQUEST: array<mixed>, _ENV: array<mixed>, param: int, local: \'foo\', random1: \'bar\'}', get_defined_vars());
40+
assertType('array{param: int, local: \'foo\', random1: \'bar\'}', get_defined_vars());
4141
} else {
4242
$random2 = "baz";
43-
assertType('array{GLOBALS: array<mixed>, _SERVER: array<mixed>, _GET: array<mixed>, _POST: array<mixed>, _FILES: array<mixed>, _COOKIE: array<mixed>, _SESSION: array<mixed>, _REQUEST: array<mixed>, _ENV: array<mixed>, param: int, local: \'foo\', random2: \'baz\'}', get_defined_vars());
43+
assertType('array{param: int, local: \'foo\', random2: \'baz\'}', get_defined_vars());
4444
}
45-
assertType('array{GLOBALS: array<mixed>, _SERVER: array<mixed>, _GET: array<mixed>, _POST: array<mixed>, _FILES: array<mixed>, _COOKIE: array<mixed>, _SESSION: array<mixed>, _REQUEST: array<mixed>, _ENV: array<mixed>, param: int, local: \'foo\', random2?: \'baz\', random1?: \'bar\'}', get_defined_vars());
45+
assertType('array{param: int, local: \'foo\', random2?: \'baz\', random1?: \'bar\'}', get_defined_vars());
4646
}

Diff for: tests/PHPStan/Rules/Debug/DebugScopeRuleTest.php

+1-56
Original file line numberDiff line numberDiff line change
@@ -21,51 +21,14 @@ public function testRuleInPhpStanNamespace(): void
2121
{
2222
$this->analyse([__DIR__ . '/data/debug-scope.php'], [
2323
[
24-
implode("\n", [
25-
'$GLOBALS (Yes): array<mixed>',
26-
'$_SERVER (Yes): array<mixed>',
27-
'$_GET (Yes): array<mixed>',
28-
'$_POST (Yes): array<mixed>',
29-
'$_FILES (Yes): array<mixed>',
30-
'$_COOKIE (Yes): array<mixed>',
31-
'$_SESSION (Yes): array<mixed>',
32-
'$_REQUEST (Yes): array<mixed>',
33-
'$_ENV (Yes): array<mixed>',
34-
'native $GLOBALS (Yes): array<mixed>',
35-
'native $_SERVER (Yes): array<mixed>',
36-
'native $_GET (Yes): array<mixed>',
37-
'native $_POST (Yes): array<mixed>',
38-
'native $_FILES (Yes): array<mixed>',
39-
'native $_COOKIE (Yes): array<mixed>',
40-
'native $_SESSION (Yes): array<mixed>',
41-
'native $_REQUEST (Yes): array<mixed>',
42-
'native $_ENV (Yes): array<mixed>',
43-
]),
24+
'Scope is empty',
4425
7,
4526
],
4627
[
4728
implode("\n", [
48-
'$GLOBALS (Yes): array<mixed>',
49-
'$_SERVER (Yes): array<mixed>',
50-
'$_GET (Yes): array<mixed>',
51-
'$_POST (Yes): array<mixed>',
52-
'$_FILES (Yes): array<mixed>',
53-
'$_COOKIE (Yes): array<mixed>',
54-
'$_SESSION (Yes): array<mixed>',
55-
'$_REQUEST (Yes): array<mixed>',
56-
'$_ENV (Yes): array<mixed>',
5729
'$a (Yes): int',
5830
'$b (Yes): int',
5931
'$debug (Yes): bool',
60-
'native $GLOBALS (Yes): array<mixed>',
61-
'native $_SERVER (Yes): array<mixed>',
62-
'native $_GET (Yes): array<mixed>',
63-
'native $_POST (Yes): array<mixed>',
64-
'native $_FILES (Yes): array<mixed>',
65-
'native $_COOKIE (Yes): array<mixed>',
66-
'native $_SESSION (Yes): array<mixed>',
67-
'native $_REQUEST (Yes): array<mixed>',
68-
'native $_ENV (Yes): array<mixed>',
6932
'native $a (Yes): int',
7033
'native $b (Yes): int',
7134
'native $debug (Yes): bool',
@@ -74,28 +37,10 @@ public function testRuleInPhpStanNamespace(): void
7437
],
7538
[
7639
implode("\n", [
77-
'$GLOBALS (Yes): array<mixed>',
78-
'$_SERVER (Yes): array<mixed>',
79-
'$_GET (Yes): array<mixed>',
80-
'$_POST (Yes): array<mixed>',
81-
'$_FILES (Yes): array<mixed>',
82-
'$_COOKIE (Yes): array<mixed>',
83-
'$_SESSION (Yes): array<mixed>',
84-
'$_REQUEST (Yes): array<mixed>',
85-
'$_ENV (Yes): array<mixed>',
8640
'$a (Yes): int',
8741
'$b (Yes): int',
8842
'$debug (Yes): bool',
8943
'$c (Maybe): 1',
90-
'native $GLOBALS (Yes): array<mixed>',
91-
'native $_SERVER (Yes): array<mixed>',
92-
'native $_GET (Yes): array<mixed>',
93-
'native $_POST (Yes): array<mixed>',
94-
'native $_FILES (Yes): array<mixed>',
95-
'native $_COOKIE (Yes): array<mixed>',
96-
'native $_SESSION (Yes): array<mixed>',
97-
'native $_REQUEST (Yes): array<mixed>',
98-
'native $_ENV (Yes): array<mixed>',
9944
'native $a (Yes): int',
10045
'native $b (Yes): int',
10146
'native $debug (Yes): bool',

0 commit comments

Comments
 (0)