Skip to content

Conversation

cedric-anne
Copy link

@cedric-anne cedric-anne commented Aug 15, 2025

This will permit to easilly assign types to global variables in an extension. Without this change, it is not possible to distinguish local variables from global variables.

Related to phpstan/phpstan#13243 and following a discussion in #4233.

Here is an exemple:

<?php

namespace MyPhpstanExtension;

use PhpParser\Node\Expr;
use PhpParser\Node\Expr\Variable;
use PHPStan\Analyser\Scope;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ExpressionTypeResolverExtension;
use PHPStan\Type\Type;

class GlobalExpressionTypeResolverExtension implements ExpressionTypeResolverExtension
{

	public function getType(Expr $expr, Scope $scope): ?Type
	{
		if (!$expr instanceof Variable || !$scope->isGlobalVariable($expr->name)) {
			return null;
		}

		if ($expr->name === 'DB') {
			return new ObjectType(\App\Db::class);
		}

		return null;
	}
}

@cedric-anne cedric-anne force-pushed the feature/global-variables branch from 4717d70 to f5cc717 Compare August 30, 2025 19:32
Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. You're not testing what this should be allowing you. In the original issue you're saying you want to override variable types with ExpressionTypeResolverExtension but you're not testing that here.
  2. Instead of setting an attribute on Variable in assignVariable, I'm thinking we could call assignExpression here
    $scope = $scope->assignVariable($var->name, new MixedType(), new MixedType(), TrinaryLogic::createYes());
    with new virtual node GlobalVariableExpr. The Scope::isGlobalVariable could check the type of the expr node for that.

Existing virtual expr nodes are defined here: https://github.com/phpstan/phpstan-src/tree/2.1.x/src/Node/Expr

Additionally, they have to be handled here https://github.com/phpstan/phpstan-src/blob/2.1.x/src/Node/Printer/Printer.php and here

if ($node instanceof GetIterableKeyTypeExpr) {
return $this->getIterableKeyType($this->getType($node->getExpr()));
}
if ($node instanceof GetIterableValueTypeExpr) {
return $this->getIterableValueType($this->getType($node->getExpr()));
}
if ($node instanceof GetOffsetValueTypeExpr) {
return $this->getType($node->getVar())->getOffsetValueType($this->getType($node->getDim()));
}
if ($node instanceof ExistingArrayDimFetch) {
return $this->getType(new Expr\ArrayDimFetch($node->getVar(), $node->getDim()));
}
if ($node instanceof UnsetOffsetExpr) {
return $this->getType($node->getVar())->unsetOffset($this->getType($node->getDim()));
}
.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants