Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 26 additions & 5 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,8 @@ final class MutatingScope implements Scope

private const KEEP_VOID_ATTRIBUTE_NAME = 'keepVoid';

private const IS_GLOBAL_ATTRIBUTE_NAME = 'isGlobal';

/** @var Type[] */
private array $resolvedTypes = [];

Expand Down Expand Up @@ -584,10 +586,25 @@ public function afterOpenSslCall(string $openSslFunctionName): self
);
}

/** @api */
public function isGlobalVariable(string $variableName): bool
{
if ($this->isSuperglobalVariable($variableName)) {
return true;
}

$varExprString = '$' . $variableName;
if (!isset($this->expressionTypes[$varExprString])) {
return false;
}

return $this->expressionTypes[$varExprString]->getExpr()->getAttribute(self::IS_GLOBAL_ATTRIBUTE_NAME) === true;
}

/** @api */
public function hasVariableType(string $variableName): TrinaryLogic
{
if ($this->isGlobalVariable($variableName)) {
if ($this->isSuperglobalVariable($variableName)) {
return TrinaryLogic::createYes();
}

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

$varExprString = '$' . $variableName;
if (!array_key_exists($varExprString, $this->expressionTypes)) {
if ($this->isGlobalVariable($variableName)) {
if ($this->isSuperglobalVariable($variableName)) {
return new ArrayType(new BenevolentUnionType([new IntegerType(), new StringType()]), new MixedType(true));
}
return new MixedType();
Expand Down Expand Up @@ -679,7 +696,7 @@ public function getMaybeDefinedVariables(): array
return $variables;
}

private function isGlobalVariable(string $variableName): bool
private function isSuperglobalVariable(string $variableName): bool
{
return in_array($variableName, self::SUPERGLOBAL_VARIABLES, true);
}
Expand Down Expand Up @@ -4168,9 +4185,13 @@ public function isUndefinedExpressionAllowed(Expr $expr): bool
return array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions);
}

public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty): self
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty, bool $isGlobal = false): self
{
$node = new Variable($variableName);
if ($isGlobal || $this->isGlobalVariable($variableName)) {
$node->setAttribute(self::IS_GLOBAL_ATTRIBUTE_NAME, true);
}

$scope = $this->assignExpression($node, $type, $nativeType);
if ($certainty->no()) {
throw new ShouldNotHappenException();
Expand Down Expand Up @@ -4945,7 +4966,7 @@ private function createConditionalExpressions(
private function mergeVariableHolders(array $ourVariableTypeHolders, array $theirVariableTypeHolders): array
{
$intersectedVariableTypeHolders = [];
$globalVariableCallback = fn (Node $node) => $node instanceof Variable && is_string($node->name) && $this->isGlobalVariable($node->name);
$globalVariableCallback = fn (Node $node) => $node instanceof Variable && is_string($node->name) && $this->isSuperglobalVariable($node->name);
$nodeFinder = new NodeFinder();
foreach ($ourVariableTypeHolders as $exprString => $variableTypeHolder) {
if (isset($theirVariableTypeHolders[$exprString])) {
Expand Down
2 changes: 1 addition & 1 deletion src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -1965,7 +1965,7 @@ static function (Node $node, Scope $scope) use ($nodeCallback): void {
continue;
}

$scope = $scope->assignVariable($var->name, new MixedType(), new MixedType(), TrinaryLogic::createYes());
$scope = $scope->assignVariable($var->name, new MixedType(), new MixedType(), TrinaryLogic::createYes(), true);
$vars[] = $var->name;
}
$scope = $this->processVarAnnotation($scope, $vars, $stmt);
Expand Down
2 changes: 2 additions & 0 deletions src/Analyser/Scope.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ public function getFunctionName(): ?string;

public function getParentScope(): ?self;

public function isGlobalVariable(string $variableName): bool;

public function hasVariableType(string $variableName): TrinaryLogic;

public function getVariableType(string $variableName): Type;
Expand Down
52 changes: 52 additions & 0 deletions tests/PHPStan/Analyser/GlobalVariableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php declare(strict_types = 1);

namespace PHPStan\Analyser;

use PhpParser\Node;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Testing\TypeInferenceTestCase;

class GlobalVariableTest extends TypeInferenceTestCase
{

public function testGlobalVariableInScript(): void
{
self::processFile(__DIR__ . '/data/global-in-script.php', function (Node $node, Scope $scope): void {
if (!($node instanceof Return_)) {
return;
}

$this->assertTrue($scope->isGlobalVariable('FOO'));
$this->assertFalse($scope->isGlobalVariable('whatever'));
});
}

public function testGlobalVariableInFunction(): void
{
self::processFile(__DIR__ . '/data/global-in-function.php', function (Node $node, Scope $scope): void {
if (!($node instanceof Return_)) {
return;
}

$this->assertFalse($scope->isGlobalVariable('BAR'));
$this->assertTrue($scope->isGlobalVariable('CONFIG'));
$this->assertFalse($scope->isGlobalVariable('localVar'));
});
}

public function testGlobalVariableInClassMethod(): void
{
self::processFile(__DIR__ . '/data/global-in-class-method.php', function (Node $node, Scope $scope): void {
if (!($node instanceof Return_)) {
return;
}

$this->assertFalse($scope->isGlobalVariable('count'));
$this->assertTrue($scope->isGlobalVariable('GLB_A'));
$this->assertTrue($scope->isGlobalVariable('GLB_B'));
$this->assertFalse($scope->isGlobalVariable('key'));
$this->assertFalse($scope->isGlobalVariable('step'));
});
}

}
16 changes: 16 additions & 0 deletions tests/PHPStan/Analyser/data/global-in-class-method.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

class ClassForGlobalTest
{

public function doSomething(int $count = 3): bool
{
global $GLB_A, $GLB_B;

foreach ([1, 2, 3] as $key => $step) {
break;
}

return false;
}
}
12 changes: 12 additions & 0 deletions tests/PHPStan/Analyser/data/global-in-function.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?php

global $BAR;

function globalTest(string $BAR): void
{
global $CONFIG;

$localVar = true;

return;
}
8 changes: 8 additions & 0 deletions tests/PHPStan/Analyser/data/global-in-script.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

global $FOO;

$FOO = "bar";
$whatever = 15;

return;
Loading