A PHPDoc type is what’s written in place of [Type] in annotations like @var [Type] or @param [Type] $foo. Learn more about PHPDoc basics »
Would you like to use advanced PHPDoc types in 3rd party code you have in /vendor? You can! Check out Stub Files »
Basic types #
int,integerstringarray-keybool,booleantruefalsenullfloatdoublenumbernumericscalar,empty-scalar,non-empty-scalaremptyarray,associative-arrayiterablecallable,pure-callable,pure-Closureresource,closed-resource,open-resourcevoidobject,callable-object,callable-array
Mixed #
mixed type can be used if we don’t want to define a more specific type. PHPStan doesn’t check anything on the mixed type - any property or method can be called on it and it can be passed to any type in a function/method call.
non-empty-mixed is a mixed that excludes falsy values (false, 0, 0.0, '', '0', [], and null).
PHPStan has a concept of implicit and explicit mixed. Missing typehint is implicit mixed - no type was specified as a parameter type or a return type. Explicit mixed is written in the PHPDoc. PHPStan’s rule level 6 isn’t satisfied with implicit mixed, but an explicit one is sufficient.
Rule level 9 is stricter about the mixed type. The only allowed operation you can do with it is to pass it to another mixed.
Classes and interfaces #
A fully-qualified name (FQN) like \Foo\Bar\Baz, or a relative name like Baz resolved based on the current namespace and use statements can be used.
self refers to the current class and parent refers to the parent class, just like in PHP itself.
Trait names cannot be used in PHPDocs, as they don’t work as native PHP typehints either.
Integer ranges #
positive-intnegative-intnon-positive-intnon-negative-intnon-zero-intint<0, 100>int<min, 100>int<50, max>
Float ranges #
positive-floatnegative-floatnon-positive-floatnon-negative-floatfinite-floatfloat<0, 1.5>float<min, 100.0>float<0.5, max>
finite-float is any float except INF, -INF, and NAN. Bounds of float<min, max> are inclusive and can be specified as integer or float literals.
General arrays #
Type[]array<Type>array<int, Type>non-empty-array<Type>non-empty-array<int, Type>
Lists #
list<Type>non-empty-list<Type>
Lists are arrays with sequential integer keys starting at 0.
Key and value types of arrays and iterables #
key-of<Type::ARRAY_CONST>value-of<Type::ARRAY_CONST>
class Foo {
public const WHEELER = [
'car' => 4,
'bike' => 2,
];
}
/**
* @param key-of<Foo::WHEELER> $type
* @param value-of<Foo::WHEELER> $wheels
*/
function repair(string $type, int $wheels): void
{
// $type is 'bike'|'car'
// $wheels is 2|4
}
Additionally value-of<BackedEnum> is supported:
enum Suit: string
{
case Hearts = 'H';
case Spades = 'S';
}
/**
* @param array<value-of<Suit>, int> $count
*/
function foo(array $count): void
{
// $count is array<'H'|'S', int>
}
Iterables #
iterable<ValueType>iterable<KeyType, ValueType>Collection<Type>Collection<int, Type>Collection|Type[]
These notations specify the iterable key and value types in a foreach statement.
These iterable rules are applied only when the Collection type isn’t generic. When it’s generic, generics rules for class-level type variables are applied.
If PHPStan encounters Collection|Foo[], two possible paths are taken:
CollectionimplementsTraversablesoCollection|Foo[]is interpreted as aCollectionobject that iterates overFoo. The array part isn’t applied.Collectiondoes not implementTraversablesoCollection|Foo[]is interpreted as aCollectionobject or an array ofFooobjects.
If Collection|Foo[] means “Collection or array” in your case even if Collection implements Traversable, you need to disambiguate the type by using Collection|array<Foo> instead.
Union types #
Written as Type1|Type2. Read more about union types here »
Intersection types #
Written as Type1&Type2. Read more about intersection types here »
Parentheses #
Parentheses can be used to disambiguate types: (Type1&Type2)|Type3
static and $this #
To denote that a method returns the same type it’s called on, use @return static or @return $this.
This is useful if we want to tell that a method from a parent class will return an object of the child class when the parent class is extended (see example).
A narrower @return $this instead of @return static can also be used, and PHPStan will check if you’re really returning the same object instance and not just an object of the child class.
When used inside a generic class, static<T> can be used to specify type arguments for the returned static type:
/**
* @template T
*/
class Collection
{
/**
* @param T $item
* @return static<T>
*/
public function add($item): static
{
// ...
}
}
Generics #
When a class or function is generic, you specify its type arguments in angle brackets: Collection<int>, Map<string, User>. These types flow through the analysis and PHPStan verifies they are used consistently.
Generic type syntax:
Collection<Type>— a generic class with a single type argumentMap<string, Type>— a generic class with multiple type argumentsCollection<covariant Type>— call-site covariance (accepts subtypes)Collection<contravariant Type>— call-site contravariance (accepts supertypes)Collection<*>— star projection (any type argument, both read and write restricted)
Many built-in PHP classes like ArrayIterator, SplObjectStorage, ReflectionClass, WeakMap, and others are made generic through PHPStan’s stub files. For example, ReflectionClass<Foo> knows it reflects the Foo class and ReflectionClass::newInstance() returns Foo.
See also Generics By Examples ».
Conditional return types #
A simpler alternative to generics if you just want to infer the return type based on if-else logic.
/**
* @return ($size is positive-int ? non-empty-array : array)
*/
function fillArray(int $size): array
{
...
}
The condition can also be negated with is not:
/**
* @return ($value is not null ? string : never)
*/
function stringify(mixed $value): string
{
...
}
It can be combined with generics as well in both the condition and the if-else types:
/**
* @template T of int|array<int>
* @param T $id
* @return (T is int ? static : array<static>)
*/
public function fetch(int|array $id)
{
...
}
Utility types for generics #
template-type can be used to get @template type from a passed object argument. Related discussion here.
new can be used to create an object type from a class-string type.
class-string #
class-string type can be used wherever a valid class name string is expected. Generic variant class-string<T> also works, or you can use class-string<Foo> to only accept valid class names that are subtypes of Foo.
interface-string and trait-string are aliases for class-string. The generic variant interface-string<T> also works.
enum-string accepts class name strings of enums (equivalent to class-string<UnitEnum>). The generic variant enum-string<T> further narrows the accepted enum type.
/**
* @param class-string $className
*/
function foo(string $className): void { ... }
Both literal strings with valid class names ('stdClass') and class constants (\stdClass::class) are accepted as class-string arguments.
If you have a general string and want to pass it as a class-string argument, you need to make sure the string contains a valid class name:
function bar(string $name): void
{
if (class_exists($name)) { // or interface_exists()
// $name is class-string here
foo($name);
}
}
Other advanced string types #
callable-string is a string that PHP considers a valid callable.
numeric-string is a string that would pass an is_numeric() check.
non-empty-string is any string except ''. It does not mean “empty” in the weird sense used by empty().
non-falsy-string (also known as truthy-string) is any string that is true after casting to boolean.
Security-focused literal-string is inspired by the is_literal() RFC. In short, it means a string that is either written by a developer or composed only of developer-written strings.
lowercase-string accepts strings where strtolower($string) === $string is true.
uppercase-string accepts strings where strtoupper($string) === $string is true.
decimal-int-string accepts strings that contain a decimal integer representation and are cast to an integer when used as an array key (e.g. '0', '123', '-1').
non-decimal-int-string accepts strings that are not decimal integers and stay as strings when used as an array key (e.g. '+1', '00', '1.2', 'foo').
You can narrow a string to these types yourself by checking whether it survives an int round-trip:
if ((string) (int) $x === $x) {
// $x is decimal-int-string
} else {
// $x is non-decimal-int-string
}
Combined string types are also supported: non-empty-lowercase-string, non-empty-uppercase-string, and non-empty-literal-string.
Global type aliases #
Type aliases (also known as typedef) are a popular feature in other languages like TypeScript or C++. Defining type aliases will allow you to reference complex types in your PHPDocs by their alias.
You can define global type aliases in the configuration file:
parameters:
typeAliases:
Name: 'string'
NameResolver: 'callable(): string'
NameOrResolver: 'Name|NameResolver'
Then you can use these aliases in your codebase:
/**
* @param NameOrResolver $arg
*/
function foo($arg)
{
// $arg is string|(callable(): string)
}
Local type aliases #
You can also define and use local aliases in PHPDocs using the @phpstan-type annotation. These are scoped to the class that defines them:
/**
* @phpstan-type UserAddress array{street: string, city: string, zip: string}
*/
class User
{
/**
* @var UserAddress
*/
private $address; // is of type array{street: string, city: string, zip: string}
}
To use a local type alias elsewhere, you can import it using the @phpstan-import-type annotation in another class’ PHPDocs:
/**
* @phpstan-import-type UserAddress from User
*/
class Order
{
/** @var UserAddress */
private $deliveryAddress; // is of type array{street: string, city: string, zip: string}
}
You can optionally change the name of the imported alias:
/**
* @phpstan-import-type UserAddress from User as DeliveryAddress
*/
class Order
{
/** @var DeliveryAddress */
private $deliveryAddress; // is of type array{street: string, city: string, zip: string}
}
Array shapes #
This feature enables usage of strong types in codebases where arrays of various specific shapes are passed around functions and methods. PHPStan checks that the values in specified keys have the correct types:
array{'foo': int, "bar": string}array{'foo': int, "bar"?: string}(keybaris optional in the array)array{int, int}(keys are0and1, also known as a tuple)array{0: int, 1?: int}(key1is optional in the array)array{foo: int, bar: string}(quotes around array keys for simple strings aren’t necessary)array{Foo::BAR: int}(class constant as a key)array{Foo::class: int}(classconstant as a key)