PHPDocs are a big part of what makes PHPStan work. PHP in its most recent versions can express a lot of things in the native typehints, but it still leaves a lot of room for PHPDocs to augment the information.
Valid PHPDocs start with /**. Variants starting only with /* or line comments // are not considered PHPDocs. [1]
Learn more about PHPDoc types » you can use in the tags described below.
Would you like to fix incorrect PHPDocs in 3rd party code you have in /vendor? You can! Check out Stub Files »
Methods and functions #
This is how a valid PHPDoc above a function or a method can look like:
/**
* @param Foo $param
* @return Bar
*/
function foo($param) { ... }
Properties #
PHPDocs can be written above class properties to denote their type:
/**
* @var Foo
*/
private $bar;
Inline @var #
Casting a type using an inline @var PHPDocs should be used only as a last resort. It can be used in a variable assignment like this:
/** @var Foo $foo */
$foo = createFoo();
This is usually done if the symbol on the right side of the = operator has a wrong type, or isn’t specific enough. Usage of inline @var is problematic for a couple of reasons:
- Because it might be used to correct wrong type information of the called symbol, PHPStan always trusts it. But if there’s a mistake in this annotation, the analysis of the code below might be wrong.
- The inline
@varneeds to be repeated above all usages of the symbol which leads to repetition in the codebase.
Instead, the type should be fixed at its source. If the called symbol comes from 3rd party code, you can correct it using a stub file. If the return type differs in each call based on the passed arguments, you can use generics, or write a dynamic return type extension instead.
If you really need to use an inline @var, consider an alternative - an assert() call, which can throw an exception at runtime, so the code execution doesn’t continue if the requirements aren’t met.
$foo = createFoo();
assert($foo instanceof Foo);
Magic properties #
For custom __get/__set methods logic, a @property PHPDoc tag can be placed above a class. If the property is only supposed to be read or written to, @property-read/@property-write variants can be used.
/**
* @property int $foo
* @property-read string $bar
* @property-write \stdClass $baz
*/
class Foo { ... }
The @property tag can also be used to override wrong property type from a parent class.
Magic methods #
For custom __call methods logic, a @method PHPDoc tag can be placed above a class:
/**
* @method int computeSum(int $a, int $b)
* @method void doSomething()
* @method static int staticMethod()
* @method int doMagic(int $a, int $b = 123)
*/
class Foo { ... }
Exceptions #
Functions and methods can be marked as throwing an exception with @throws:
/**
* @throws \InvalidArgumentException
*/
function doFoo(): void
{
// ...
}
This is useful for precise analysis of try-catch-finally blocks, and also for bringing exceptions under control by enforcing documentation and handling of checked exceptions.
Callables #
Aside from describing callable signatures in PHPDoc types, PHPStan also supports declaring whether the callable is executed immediately or saved for later when passed into a function or a method.
By default PHPStan considers callables passed into function calls to be executed immediately, and callables passed into object method calls to be executed later. These defaults can be overridden with @param-immediately-invoked-callable and @param-later-invoked-callable PHPDoc tags:
/**
* @param-later-invoked-callable $cb
*/
function acceptCallableAndCallLater(callable $cb): void
{
// ...
}
class Foo
{
/**
* @param-immediately-invoked-callable $cb
*/
public function acceptAndCallCallableNow(callable $cb): void
{
}
}
When passing a closure (an anonymous function or an arrow function) into a function/method that binds the closure to a different object, changing the meaning of $this inside the closure, you can use @param-closure-this PHPDoc tag to declare what $this is going to be when this closure is called:
/**
* @param-closure-this Bar $cb
*/
function doFoo(Closure $cb)
{
$cb->bindTo(new Bar());
// ...
}
doFoo(function () {
// $this is Bar
});
PHPStan also supports changing the meaning of $this inside a closure with an extension.
Mixins #
When a class delegates unknown method calls and property accesses to a different class using __call and __get/__set, we can describe the relationship using @mixin PHPDoc tag:
class A
{
public function doA(): void
{
}
}
/**
* @mixin A
*/
class B
{
public function doB(): void
{
}
public function __call($name, $arguments)
{
(new A())->$name(...$arguments);
}
}
$b = new B();
$b->doB();
$b->doA(); // works
It also works with generics:
/**
* @template T
* @mixin T
*/
class Delegatee
{
/** @var T */
private $delegate;
/**
* @param T $delegate
*/
public function __construct($delegate)
{
$this->delegate = $delegate;
}
public function __call($name, $arguments)
{
return $this->delegate->$name(...$arguments);
}
}
$d = new Delegatee(new \Exception('My message'));
echo $d->getMessage(); // PHPStan knows the method is on Exception
Combining PHPDoc types with native typehints #
PHPDocs can also complement native typehints with additional information. The most common use-case is telling PHPStan what’s in an array:
/**
* @param User[] $users
*/
function