PHPStan can sometimes resolve the type of an expression to something that you didn’t expect. This guide is here to help you troubleshoot where the unexpected type might be coming from. Here’s the list of all the places PHPStan is getting its type information from.
Native types #
PHPStan uses native parameter types, return types, and property types to inform the analysis about types.
private int $prop;
function foo(Foo $foo): Bar
PHPDoc types #
@return among others are used to convey the same types that can be expressed natively and are checked in PHP runtime (like
object for example), but also additional types that are meaningful only when checked by the static analyser.
These types are interpreted alongside native types and combined together when both are present:
/** @param array<int, Item> $items */
function foo(array $items)
Stub files #
Besides the original code that’s analysed, PHPStan also takes advantage of so-called stub files that are used for overriding wrong 3rd party PHPDocs. These stub files can come from PHPStan extensions, but you can also write your own directly in the project.
Internal stubs #
PHPStan needs to know precise parameter and return types of built-in PHP functions and those coming from loaded PHP extensions. There are three separate sources for that:
functionMap.php, and deltas for different PHP versions
- Official stubs extracted from php-src if you’re on PHP 8 and later
- For custom PHP extensions that are not present in
jetbrains/phpstorm-stubs, their PHP reflection data is used.
The way these are combined together to offer the full picture is a very complex logic and subject to frequent change as bugs are getting fixed and improvements made.
Local type narrowing #
There are many ways how to narrow a type locally in a function body. This guide talks about that.
Dynamic return type extensions #
You can use
PHPStan\dumpType() function in your code to see what type PHPStan resolve an expression to:
\PHPStan\dumpType(1 + 1); // Reports: Dumped type: 2
Re-run the PHPStan analysis on the code that has this function call to see the result.
Of course this function should never become part of code running in production, so don’t forget to delete it!
What PHPStan doesn’t do #
PHPStan doesn’t descend into a called function’s body to see what’s going on there. The native types, the PHPDocs, and the registered dynamic return type and type-specifying extensions are everything that’s used to figure out how the types are going to impacted.