phpstan-src icon indicating copy to clipboard operation
phpstan-src copied to clipboard

Indicates whether a variable is global

Open cedric-anne opened this issue 5 months ago • 3 comments

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 https://github.com/phpstan/phpstan/issues/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 avatar Aug 15 '25 19:08 cedric-anne

  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.

I added a test for this use case.

  1. Instead of setting an attribute on Variable in assignVariable, I'm thinking we could call assignExpression [...] with new virtual node GlobalVariableExpr. The Scope::isGlobalVariable could check the type of the expr node for that.

I struggle to find how to make it work, probably because I am not familiar with the PHPStan internal logic, so maybe I missed something.

I finally found a solution with an early call to the ExpressionTypeResolverExtensionRegistry here: https://github.com/phpstan/phpstan-src/blob/db78deb7255de62d0d253d7cfd901c91b65b58e3/src/Analyser/MutatingScope.php#L4292-L4301

This is the only solution I found to be able to assign the type during the Global_ statement node parsing.

cedric-anne avatar Sep 03 '25 20:09 cedric-anne

I think you need to rebase/fix some conflict @cedric-anne

VincentLanglet avatar Sep 14 '25 09:09 VincentLanglet

I think you need to rebase/fix some conflict @cedric-anne

Done. Conflicts were only related to use statements.

I am not entirely satisfied with what I have proposed, but I was waiting for a review from someone who knows more about the internal logic of PHPStan to find out if I had missed something obvious, or even if I was heading in the right direction. Rather than using the ExpressionTypeResolverExtension extensions, I think it might be better to declare a new extension interface GlobalVariableTypeSpecifyingExtension with a public function getGlobalVariableType(GlobalVariableExpr $globalVariable, Scope $scope): ?Type method.

Also, maybe this should be splitted into two distinct PRs. Indeed, being able to know whether a variable is global and being able to define its type using an extension are two distinct features.

cedric-anne avatar Sep 14 '25 13:09 cedric-anne