PHPStan 2.0 is released! Upgrade today and buy the PHPStan elephpant!
Menu

Type-Specifying Extensions

These extensions allow you to specify types of expressions based on certain type-checking function and method calls, like is_int() or self::assertNotNull().

The implementation is all about applying the core concepts so check out that guide first and then continue here.

if (is_int($variable)) {
    // here we can be sure that $variable is integer
}
// using PHPUnit's asserts

self::assertNotNull($variable);
// here we can be sure that $variable is not null

This is the the interface for the type-specifying extension:

namespace PHPStan\Type;

use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;
use PHPStan\Analyser\SpecifiedTypes;
use PHPStan\Analyser\TypeSpecifierContext;
use PHPStan\Reflection\MethodReflection;

interface StaticMethodTypeSpecifyingExtension
{

	public function getClass(): string;

	public function isStaticMethodSupported(
		MethodReflection $staticMethodReflection,
		StaticCall $node,
		TypeSpecifierContext $context
	): bool;

	public function specifyTypes(
		MethodReflection $staticMethodReflection,
		StaticCall $node,
		Scope $scope,
		TypeSpecifierContext $context
	): SpecifiedTypes;

}

Type-specifying extension cannot have PHPStan\Analyser\TypeSpecifier injected in the constructor due to circular reference issue, but the extensions can implement PHPStan\Analyser\TypeSpecifierAwareExtension interface to obtain TypeSpecifier via a setter.

This is how you’d write the extension for the second example above:

public function getClass(): string
{
	return \PHPUnit\Framework\Assert::class;
}

public function isStaticMethodSupported(
	MethodReflection $staticMethodReflection,
	StaticCall $node,
	TypeSpecifierContext $context
): bool
{
	// The $context argument tells us if we're in an if condition or not (as in this case).
	// Is assertNotNull called with at least 1 argument?
	return $staticMethodReflection->getName() === 'assertNotNull' && $context->null() && isset($node->getArgs()[0]);
}

public function specifyTypes(
	MethodReflection $staticMethodReflection,
	StaticCall $node,
	Scope $scope,
	TypeSpecifierContext $context
): SpecifiedTypes
{
	$expr = $node->getArgs()[0]->value;
	$typeBefore = $scope->getType($expr);
	$type = TypeCombinator::removeNull($typeBefore);

	// Assuming extension implements \PHPStan\Analyser\TypeSpecifierAwareExtension

	return $this->typeSpecifier->create($expr, $type, TypeSpecifierContext::createTruthy());
}

And finally, register the extension to PHPStan in the configuration file:

services:
	-
		class: App\PHPStan\AssertNotNullTypeSpecifyingExtension
		tags:
			- phpstan.typeSpecifier.staticMethodTypeSpecifyingExtension

There’s also analogous functionality for:

Edit this page on GitHub

Theme
A
© 2016–2024 Ondřej Mirtes