diff --git a/conf/config.neon b/conf/config.neon index 486a0e2851..15a8482007 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -607,8 +607,6 @@ services: - class: PHPStan\Analyser\ScopeFactory - arguments: - explicitMixedForGlobalVariables: %featureToggles.explicitMixedForGlobalVariables% - class: PHPStan\Analyser\NodeScopeResolver diff --git a/src/Analyser/DirectInternalScopeFactory.php b/src/Analyser/DirectInternalScopeFactory.php index 701b06dbbf..e2b88a406d 100644 --- a/src/Analyser/DirectInternalScopeFactory.php +++ b/src/Analyser/DirectInternalScopeFactory.php @@ -34,11 +34,20 @@ public function __construct( private bool $treatPhpDocTypesAsCertain, private PhpVersion $phpVersion, private bool $explicitMixedInUnknownGenericNew, + private bool $explicitMixedForGlobalVariables, private ConstantResolver $constantResolver, ) { } + /** + * @param array $expressionTypes + * @param array $nativeExpressionTypes + * @param array $conditionalExpressions + * @param array<(FunctionReflection|MethodReflection)> $inFunctionCallsStack + * @param array $currentlyAssignedExpressions + * @param array $currentlyAllowedUndefinedExpressions + */ public function create( ScopeContext $context, bool $declareStrictTypes = false, @@ -93,6 +102,7 @@ public function create( $parentScope, $nativeTypesPromoted, $this->explicitMixedInUnknownGenericNew, + $this->explicitMixedForGlobalVariables, ); } diff --git a/src/Analyser/InternalScopeFactory.php b/src/Analyser/InternalScopeFactory.php index 6a19d4cc25..13ed62fe68 100644 --- a/src/Analyser/InternalScopeFactory.php +++ b/src/Analyser/InternalScopeFactory.php @@ -15,7 +15,7 @@ interface InternalScopeFactory * @param array $conditionalExpressions * @param array $currentlyAssignedExpressions * @param array $currentlyAllowedUndefinedExpressions - * @param array $inFunctionCallsStack + * @param array $inFunctionCallsStack */ public function create( ScopeContext $context, diff --git a/src/Analyser/LazyInternalScopeFactory.php b/src/Analyser/LazyInternalScopeFactory.php index 9bceec4ba3..bd8d6d92fd 100644 --- a/src/Analyser/LazyInternalScopeFactory.php +++ b/src/Analyser/LazyInternalScopeFactory.php @@ -22,6 +22,8 @@ class LazyInternalScopeFactory implements InternalScopeFactory private bool $explicitMixedInUnknownGenericNew; + private bool $explicitMixedForGlobalVariables; + /** * @param class-string $scopeClass */ @@ -32,8 +34,18 @@ public function __construct( { $this->treatPhpDocTypesAsCertain = $container->getParameter('treatPhpDocTypesAsCertain'); $this->explicitMixedInUnknownGenericNew = $this->container->getParameter('featureToggles')['explicitMixedInUnknownGenericNew']; + $this->explicitMixedForGlobalVariables = $this->container->getParameter('featureToggles')['explicitMixedForGlobalVariables']; } + /** + * @param array $expressionTypes + * @param array $nativeExpressionTypes + * @param array $conditionalExpressions + * @param array $currentlyAssignedExpressions + * @param array $currentlyAllowedUndefinedExpressions + * @param array<(FunctionReflection|MethodReflection)> $inFunctionCallsStack + * + */ public function create( ScopeContext $context, bool $declareStrictTypes = false, @@ -88,6 +100,7 @@ public function create( $parentScope, $nativeTypesPromoted, $this->explicitMixedInUnknownGenericNew, + $this->explicitMixedForGlobalVariables, ); } diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index 752b152396..fcbe29f293 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -188,6 +188,7 @@ public function __construct( private ?Scope $parentScope = null, private bool $nativeTypesPromoted = false, private bool $explicitMixedInUnknownGenericNew = false, + private bool $explicitMixedForGlobalVariables = false, ) { if ($namespace === '') { @@ -465,6 +466,10 @@ public function afterOpenSslCall(string $openSslFunctionName): self /** @api */ public function hasVariableType(string $variableName): TrinaryLogic { + if ($this->isGlobalVariable($variableName)) { + return TrinaryLogic::createYes(); + } + $varExprString = '$' . $variableName; if (!isset($this->expressionTypes[$varExprString])) { if ($this->canAnyVariableExist()) { @@ -495,6 +500,10 @@ public function getVariableType(string $variableName): Type } } + if ($this->isGlobalVariable($variableName)) { + return new ArrayType(new StringType(), new MixedType($this->explicitMixedForGlobalVariables)); + } + if ($this->hasVariableType($variableName)->no()) { throw new UndefinedVariableException($this, $variableName); } @@ -528,6 +537,21 @@ public function getDefinedVariables(): array return $variables; } + private function isGlobalVariable(string $variableName): bool + { + return in_array($variableName, [ + 'GLOBALS', + '_SERVER', + '_GET', + '_POST', + '_FILES', + '_COOKIE', + '_SESSION', + '_REQUEST', + '_ENV', + ], true); + } + /** @api */ public function hasConstant(Name $name): bool { @@ -2109,6 +2133,7 @@ public function doNotTreatPhpDocTypesAsCertain(): Scope $this->parentScope, false, $this->explicitMixedInUnknownGenericNew, + $this->explicitMixedForGlobalVariables, ); } @@ -2405,8 +2430,12 @@ public function enterClass(ClassReflection $classReflection): self $this->isDeclareStrictTypes(), null, $this->getNamespace(), - array_merge($this->getSuperglobalTypes(), $this->getConstantTypes(), ['$this' => $thisHolder]), - array_merge($this->getNativeSuperglobalTypes(), $this->getNativeConstantTypes(), ['$this' => $thisHolder]), + array_merge($this->getConstantTypes(), [ + '$this' => $thisHolder, + ]), + array_merge($this->getNativeConstantTypes(), [ + '$this' => $thisHolder, + ]), [], null, null, @@ -2642,8 +2671,8 @@ private function enterFunctionLike( $this->isDeclareStrictTypes(), $functionReflection, $this->getNamespace(), - array_merge($this->getSuperglobalTypes(), $this->getConstantTypes(), $expressionTypes), - array_merge($this->getNativeSuperglobalTypes(), $this->getNativeConstantTypes(), $nativeExpressionTypes), + array_merge($this->getConstantTypes(), $expressionTypes), + array_merge($this->getNativeConstantTypes(), $nativeExpressionTypes), ); } @@ -2655,8 +2684,6 @@ public function enterNamespace(string $namespaceName): self $this->isDeclareStrictTypes(), null, $namespaceName, - $this->getSuperglobalTypes(), - $this->getNativeSuperglobalTypes(), ); } @@ -2880,8 +2907,8 @@ private function enterAnonymousFunctionWithoutReflection( $this->isDeclareStrictTypes(), $this->getFunction(), $this->getNamespace(), - array_merge($this->getSuperglobalTypes(), $this->getConstantTypes(), $expressionTypes), - array_merge($this->getNativeSuperglobalTypes(), $this->getNativeConstantTypes(), $nativeTypes), + array_merge($this->getConstantTypes(), $expressionTypes), + array_merge($this->getNativeConstantTypes(), $nativeTypes), [], $this->inClosureBindScopeClass, new TrivialParametersAcceptor(), @@ -4963,34 +4990,4 @@ private function getNativeConstantTypes(): array return $constantTypes; } - /** @return array */ - private function getSuperglobalTypes(): array - { - $superglobalTypes = []; - $exprStrings = ['$GLOBALS', '$_SERVER', '$_GET', '$_POST', '$_FILES', '$_COOKIE', '$_SESSION', '$_REQUEST', '$_ENV']; - foreach ($this->expressionTypes as $exprString => $typeHolder) { - if (!in_array($exprString, $exprStrings, true)) { - continue; - } - - $superglobalTypes[$exprString] = $typeHolder; - } - return $superglobalTypes; - } - - /** @return array */ - private function getNativeSuperglobalTypes(): array - { - $superglobalTypes = []; - $exprStrings = ['$GLOBALS', '$_SERVER', '$_GET', '$_POST', '$_FILES', '$_COOKIE', '$_SESSION', '$_REQUEST', '$_ENV']; - foreach ($this->nativeExpressionTypes as $exprString => $typeHolder) { - if (!in_array($exprString, $exprStrings, true)) { - continue; - } - - $superglobalTypes[$exprString] = $typeHolder; - } - return $superglobalTypes; - } - } diff --git a/src/Analyser/ScopeFactory.php b/src/Analyser/ScopeFactory.php index ea4c761e32..c2401d375c 100644 --- a/src/Analyser/ScopeFactory.php +++ b/src/Analyser/ScopeFactory.php @@ -2,45 +2,17 @@ namespace PHPStan\Analyser; -use PhpParser\Node\Expr\Variable; -use PHPStan\Type\ArrayType; -use PHPStan\Type\MixedType; -use PHPStan\Type\StringType; - /** @api */ class ScopeFactory { - public function __construct( - private InternalScopeFactory $internalScopeFactory, - private bool $explicitMixedForGlobalVariables, - ) + public function __construct(private InternalScopeFactory $internalScopeFactory) { } public function create(ScopeContext $context): MutatingScope { - $superglobalType = new ArrayType(new StringType(), new MixedType($this->explicitMixedForGlobalVariables)); - $expressionTypes = [ - '$GLOBALS' => ExpressionTypeHolder::createYes(new Variable('GLOBALS'), $superglobalType), - '$_SERVER' => ExpressionTypeHolder::createYes(new Variable('_SERVER'), $superglobalType), - '$_GET' => ExpressionTypeHolder::createYes(new Variable('_GET'), $superglobalType), - '$_POST' => ExpressionTypeHolder::createYes(new Variable('_POST'), $superglobalType), - '$_FILES' => ExpressionTypeHolder::createYes(new Variable('_FILES'), $superglobalType), - '$_COOKIE' => ExpressionTypeHolder::createYes(new Variable('_COOKIE'), $superglobalType), - '$_SESSION' => ExpressionTypeHolder::createYes(new Variable('_SESSION'), $superglobalType), - '$_REQUEST' => ExpressionTypeHolder::createYes(new Variable('_REQUEST'), $superglobalType), - '$_ENV' => ExpressionTypeHolder::createYes(new Variable('_ENV'), $superglobalType), - ]; - - return $this->internalScopeFactory->create( - $context, - false, - null, - null, - $expressionTypes, - $expressionTypes, - ); + return $this->internalScopeFactory->create($context); } } diff --git a/src/Testing/PHPStanTestCase.php b/src/Testing/PHPStanTestCase.php index ea3ca9a52f..62e6095822 100644 --- a/src/Testing/PHPStanTestCase.php +++ b/src/Testing/PHPStanTestCase.php @@ -179,9 +179,9 @@ public function createScopeFactory(ReflectionProvider $reflectionProvider, TypeS $this->shouldTreatPhpDocTypesAsCertain(), $container->getByType(PhpVersion::class), $container->getParameter('featureToggles')['explicitMixedInUnknownGenericNew'], + $container->getParameter('featureToggles')['explicitMixedForGlobalVariables'], $constantResolver, ), - $container->getParameter('featureToggles')['explicitMixedForGlobalVariables'], ); } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 7ed6772c6a..6b8cbe3a03 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -463,7 +463,6 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/closure-types.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5219.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/strval.php'); - yield from $this->gatherAssertTypes(__DIR__ . '/data/superglobals.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/array-next.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/non-empty-string.php'); diff --git a/tests/PHPStan/Analyser/data/superglobals.php b/tests/PHPStan/Analyser/data/superglobals.php deleted file mode 100644 index 2885d59f04..0000000000 --- a/tests/PHPStan/Analyser/data/superglobals.php +++ /dev/null @@ -1,59 +0,0 @@ -', $GLOBALS); - assertType('array', $_SERVER); - assertType('array', $_GET); - assertType('array', $_POST); - assertType('array', $_FILES); - assertType('array', $_COOKIE); - assertType('array', $_SESSION); - assertType('array', $_REQUEST); - assertType('array', $_ENV); - } - - public function canBeOverwritten(): void - { - $GLOBALS = []; - assertType('array{}', $GLOBALS); - assertNativeType('array{}', $GLOBALS); - } - - public function canBePartlyOverwritten(): void - { - $GLOBALS['foo'] = 'foo'; - assertType("non-empty-array&hasOffsetValue('foo', 'foo')", $GLOBALS); - assertNativeType("non-empty-array&hasOffsetValue('foo', 'foo')", $GLOBALS); - } - - public function canBeNarrowed(): void - { - if (isset($GLOBALS['foo'])) { - assertType("array&hasOffsetValue('foo', mixed~null)", $GLOBALS); - assertNativeType("array&hasOffset('foo')", $GLOBALS); // https://github.com/phpstan/phpstan/issues/8395 - } else { - assertType('array', $GLOBALS); - assertNativeType('array', $GLOBALS); - } - assertType('array', $GLOBALS); - assertNativeType('array', $GLOBALS); - } - -} - -function functionScope() { - assertType('array', $GLOBALS); - assertNativeType('array', $GLOBALS); -} - -assertType('array', $GLOBALS); -assertNativeType('array', $GLOBALS);