Reflection
PHPStan has its own reflection layer for asking details about functions, classes, properties, methods, and constants.
Various reflection objects can be obtained either on PHPStan\Type\Type
implementations (see type system), or on PHPStan\Reflection\ReflectionProvider
object by asking for it in the constructor of your extension:
private ReflectionProvider $reflectionProvider;
public function __construct(
ReflectionProvider $reflectionProvider
)
{
$this->reflectionProvider = $reflectionProvider;
}
Functions #
There are three relevant methods about functions on ReflectionProvider
:
public function hasFunction(PhpParser\Node\Name $nameNode, ?PHPStan\Analyser\Scope $scope): bool
public function getFunction(PhpParser\Node\Name $nameNode, ?PHPStan\Analyser\Scope $scope): FunctionReflection
public function resolveFunctionName(PhpParser\Node\Name $nameNode, ?PHPStan\Analyser\Scope $scope): ?string
These methods need Scope because when calling function foo()
, PHP first checks whether function CurrentNamespace\foo
exists, and if it doesn’t, it looks for function foo
in the global namespace.
The getFunction
method returns PHPStan\Reflection\FunctionReflection
which can be used to get information about the function.
FunctionReflection
doesn’t directly contain parameters and return types, but they can be accessed through the getVariants(): ParametersAcceptor[]
method. That’s because some built-in PHP functions have multiple variants, like strtok
or implode
.
If you have the PhpParser\Node\Expr\FuncCall
expression, you can obtain the right variant by using ParametersAcceptorSelector
:
$variant = PHPStan\Reflection\ParametersAcceptorSelector::selectFromArgs(
$scope,
$funcCall->getArgs(),
$functionReflection->getVariants()
);
$parameters = $variant->getParameters();
$returnType = $variant->getReturnType();
Global constants #
There are three relevant methods about global constants on ReflectionProvider
:
public function hasConstant(PhpParser\Node\Name $nameNode, ?PHPStan\Analyser\Scope $scope): bool
public function getConstant(PhpParser\Node\Name $nameNode, ?PHPStan\Analyser\Scope $scope): GlobalConstantReflection
public function resolveConstantName(PhpParser\Node\Name $nameNode, ?PHPStan\Analyser\Scope $scope): ?string
These methods need Scope because when accessing global constant FOO
, PHP first checks whether constant CurrentNamespace\FOO
exists, and if it doesn’t, it looks for constant FOO
in the global namespace.
The getConstant
method returns PHPStan\Reflection\GlobalConstantReflection
which can be used to get information about the constant like the value type with the getValueType(): Type
method.
Classes #
The PHPStan\Reflection\ClassReflection
can be obtained using the getClass()
method on ReflectionProvider
. It’s recommended to first check that the class exists using the hasClass()
method.
ClassReflection
objects represent not only classes, but also interfaces and traits. You can differentiate between them by asking isClass()
, isInterface()
, and isTrait()
.
ClassReflection
can be used to query information about extended parent classes, implemented interfaces, and used traits:
public function getParentClass(): self|false
public function getInterfaces(): self[]
public function getTraits(): self[]
If you want to get some information that isn’t available on ClassReflection
, you can get the built-in ReflectionClass
instance [1] with the getNativeReflection(): \ReflectionClass
method.
Properties #
Property reflections can be obtained by using these methods on ClassReflection
:
public function hasProperty(string $propertyName): bool
public function getProperty(string $propertyName, PHPStan\Analyser\Scope $scope): PropertyReflection
Class reflection works closely with class reflection extensions to also return information about magically implemented properties using __get()
and __set()
. If you want to only get information about native properties with no regard to __get()
and __set()
, use ClassReflection::hasNativeProperty()
and ClassReflection::getNativeProperty()
instead of ClassReflection::hasProperty()
and ClassReflection::getProperty()
.
Property reflections can also be obtained using methods on PHPStan\Type\Type
. See type system for more details.
The returned PropertyReflection
object can be used to ask about property’s type, visibility, declaring class, and PHPDoc.
Methods #
Method reflections can be obtained by using these methods on ClassReflection
:
public function hasMethod(string $methodName): bool
public function getMethod(string $methodName, PHPStan\Analyser\Scope $scope): MethodReflection
Class reflection works closely with class reflection extensions to also return information about magically implemented methods using __call()
and __callStatic()
. If you want to only get information about native methods with no regard to __call()
and __callStatic()
, use ClassReflection::hasNativeMethod()
and ClassReflection::getNativeMethod()
instead of ClassReflection::hasMethod()
and ClassReflection::getMethod()
.
Method reflections can also be obtained using methods on PHPStan\Type\Type
. See type system for more details.
The returned MethodReflection
object can be used to ask about method’s visibility, declaring class, PHPDoc, etc.
MethodReflection
doesn’t directly contain parameters and return types, but they can be accessed through the getVariants(): ParametersAcceptor[]
method. That’s because some built-in PHP methods have multiple variants, like PDO::query()
.
If you have the PhpParser\Node\Expr\MethodCall
expression, you can obtain the right variant by using ParametersAcceptorSelector
:
$variant = PHPStan\Reflection\ParametersAcceptorSelector::selectFromArgs(
$scope,
$methodCall->getArgs(),
$methodReflection->getVariants()
);
$parameters = $variant->getParameters();
$returnType = $variant->getReturnType();
Class constants #
Class constant reflections can be obtained by using these methods on ClassReflection
:
public function hasConstant(string $name): bool
public function getConstant(string $name): ConstantReflection
Class constant reflections can also be obtained using methods on PHPStan\Type\Type
. See type system for more details.
The returned ConstantReflection
can be used to ask about constant’s visibility, declaring class, PHPDoc, and the value type with the getValueType(): Type
method.
Retrieving custom PHPDocs #
All information that PHPStan itself works with (like parameter types) is already available directly as methods on reflection objects.
If you want to write custom logic using PHPDoc tags that are unknown to PHPStan internals, you can directly access the PHPDoc’s abstract syntax tree that’s parsed and represented with phpstan/phpdoc-parser library.
Accessing the PHPDoc is a matter of getting the PHPDoc’s string that can look for example like /** @var Foo */
- reflection objects described in this article have getDocComment(): ?string
method.
Alternatively, you can obtain the PHPDoc’s string from the source code AST. All nodes have the getDocComment(): ?PhpParser\Comment\Doc
method.
Getting the PHPDoc’s AST with PHPStan\Type\FileTypeMapper
by asking for the object through your extension’s constructor:
private FileTypeMapper $fileTypeMapper;
public function __construct(FileTypeMapper $fileTypeMapper)
{
$this->fileTypeMapper = $fileTypeMapper;
}
FileTypeMapper
has getResolvedPhpDoc
method:
public function getResolvedPhpDoc(
string $fileName,
?string $className,
?string $traitName,
?string $functionName,
string $docComment
): ResolvedPhpDocBlock
Beside the PHPDoc’s string itself, you also need to provide the location of the PHPDoc. The call typically looks like this:
$resolvedPhpDoc = $this->fileTypeMapper->getResolvedPhpDoc(
$scope->getFile(),
$scope->isInClass() ? $scope->getClassReflection()->getName() : null,
$scope->isInTrait() ? $scope->getTraitReflection()->getName() : null,
$function !== null ? $function->getName() : null,
$docComment
);
It returns the ResolvedPhpDocBlock
object. The phpstan/phpdoc-parser AST can be obtained by calling getPhpDocNodes(): PhpDocNode[]
method. The whole PHPDoc is represented by a single root node. So why it returns an array of nodes? Because PHPStan merges PHPDocs from overridden methods to get the complete picture, so all of these nodes are available as a returned value from the getPhpDocNodes()
method.
In some cases you’ll get the Adapter implementation instead from BetterReflection because of the hybrid approach to static reflection. But they’re compatible. ↩︎