PHPDoc Types
PHPDoc types let static analysis tools understand array structures, generics, string constraints, type guards, and security-related data flow that cannot be fully expressed with native PHP type declarations.
This page separates broadly portable types from Psalm-specific and PHPStan-specific features. PHPDoc, Psalm, and PHPStan do not support exactly the same syntax, so public APIs should prefer shared syntax and use tool-specific tags only when needed.
How to Read This Page
| Area | Use |
|---|---|
| Common PHPDoc | Basic types understood by IDEs, phpDocumentor, Psalm, and PHPStan |
| Psalm/PHPStan common | Advanced types that are practical in both major analyzers |
| Psalm-specific | @psalm-* tags, Psalm utility types, taint analysis |
| PHPStan-specific | @phpstan-* tags, PHPStan type aliases, PHPStan-specific additions |
Basic Types
Scalar and Special Types
/** @param int $id */
/** @param positive-int $count */
/** @param non-negative-int $offset */
/** @param non-zero-int $delta */
/** @param float $amount */
/** @param string $name */
/** @param non-empty-string $label */
/** @param bool $enabled */
/** @return null */
/** @return true */
/** @return false */
/** @return mixed */
/** @return never */
/** @return void */
mixed explicitly allows any type. PHPStan distinguishes implicit mixed from explicitly documented mixed.
never describes a function that never returns normally, for example because it always throws or exits. PHPStan also treats noreturn, never-return, never-returns, and no-return as equivalent names.
Integer Ranges and Masks
/** @param positive-int $id */
/** @param negative-int $delta */
/** @param non-positive-int $max */
/** @param non-negative-int $offset */
/** @param int-mask<1, 2, 4> $flags */
/** @param int-mask-of<Foo::INT_*> $flags */
Psalm uses int-range<0, 100> for integer ranges.
/** @param int-range<0, 100> $percentage */
PHPStan uses int<0, 100>, int<min, 100>, and int<50, max>.
/** @param int<0, 100> $percentage */
String Types
/** @param numeric-string $number */
/** @param lowercase-string $lower */
/** @param non-empty-lowercase-string $lower */
/** @param literal-string $sql */
/** @param callable-string $callable */
/** @param class-string $class */
/** @param class-string<User> $userClass */
/** @param trait-string $trait */
/** @param enum-string<Suit> $enum */
PHPStan also supports non-falsy-string / truthy-string, non-empty-uppercase-string, non-empty-literal-string, and interface-string<T>. It also has decimal-int-string and non-decimal-int-string for safer string array keys. PHP converts string keys like '123' to integer keys, so strict array<string, mixed> APIs may need the safer type.
/** @param non-empty-uppercase-string $upper */
/** @param non-falsy-string $truthy */
/** @param non-empty-literal-string $fragment */
/** @param interface-string<ServiceInterface> $service */
/** @param decimal-int-string $key */
/** @param non-decimal-int-string $safeStringKey */
Object Types
/** @param object $object */
/** @param \DateTimeInterface $date */
/** @return static */
/** @return $this */
/** @return object{foo: int, bar?: string} */
/** @return object{foo: int, bar?: string}&\stdClass */
object{...} is an object shape. In PHPStan, object shape properties are read-only; intersecting with a class such as \stdClass can express writable properties.
Arrays and Lists
Generic Arrays
/** @return array<string, int> */
/** @return array<int, User> */
/** @return non-empty-array<string, User> */
/** @return iterable<int, User> */
Type[] is an older shorthand equivalent to array<array-key, Type>. Prefer array<TKey, TValue> in new code when the key type matters.
/** @param User[] $users */
/** @param array<int, User> $users */
PHPStan also supports associative-array as a basic type.
/** @return associative-array */
Lists
list<T> is an array with continuous integer keys starting at zero.
/** @param list<string> $names */
/** @return non-empty-list<User> */
Psalm and PHPStan also support list shapes.
/** @return list{string, int} */
PHPStan can also combine non-empty list syntax with shape syntax, for example non-empty-list{string, int}.
Array Shapes
Array shapes describe different value types for known keys.
/**
* @return array{
* id: positive-int,
* name: non-empty-string,
* email?: non-empty-string,
* roles: list<non-empty-string>
* }
*/
function userProfile(): array;
Psalm and PHPStan can describe unsealed/open shapes that allow extra keys.
/** @param array{verbose: bool, ...} $options */
/** @param array{foo: int, ...<string, int>} $data */
/** @return list{string, int, ...<bool>} */
PHPStan treats sealed array shapes strictly. If extra keys are intended, document that intent with an unsealed shape.
Composite Types
Union, Intersection, Parentheses
/** @param int|string $id */
/** @return User|null */
/** @param Countable&Traversable $collection */
/** @param (A&B)|C $value */
Literal and Constant Types
/** @return 'success'|'error'|'pending' */
/** @return 200|400|500 */
/** @param Foo::STATUS_* $status */
/** @param Foo::* $constant */
Callables
/** @param callable(int, string): bool $callback */
/** @param callable(string &$value): void $normalizer */
/** @param callable(float ...$values): int|null $aggregate */
/** @param Closure(User): string $formatter */
/** @param pure-callable(User): string $formatter */
/** @param pure-Closure(User): string $formatter */
PHPStan supports @param-closure-this to specify $this inside a closure.
/**
* @param Closure(): void $callback
* @param-closure-this User $callback
*/
function withUser(Closure $callback): void;
Generics
Function Templates
/**
* @template T
* @param list<T> $items
* @param callable(T): bool $predicate
* @return list<T>
*/
function filter(array $items, callable $predicate): array;
Class Templates
/**
* @template TKey of array-key
* @template TValue
* @implements IteratorAggregate<TKey, TValue>
*/
final class Collection implements IteratorAggregate
{
/** @var array<TKey, TValue> */
private array $items = [];
/** @return Traversable<TKey, TValue> */
public function getIterator(): Traversable
{
yield from $this->items;
}
}
Psalm and PHPStan support @template-covariant and @template-contravariant. PHPStan also supports call-site variance such as Collection<covariant Animal> and Collection<contravariant Dog>, plus star projection such as Collection<*>.
Conditional Types
/**
* @template T of int|array<int>
* @param T $id
* @return (T is int ? User : list<User>)
*/
function fetch(int|array $id): User|array;
PHPStan can also express negated conditions.
/**
* @return ($value is not null ? string : never)
*/
function stringify(mixed $value): string;
Type Operators
key-of / value-of
/**
* @param key-of<User::FIELDS> $field
* @return value-of<User::FIELDS>
*/
function fieldValue(string $field): string|int;
In PHPStan, value-of<BackedEnum> can extract a backed enum’s value type.
/** @param value-of<Suit> $suit */
function selectSuit(string $suit): void;
Offset Access
/**
* @template T of array<string, mixed>
* @template K of key-of<T>
* @param T $data
* @param K $key
* @return T[K]
*/
function get(array $data, string $key): mixed
{
return $data[$key];
}
Psalm Utility Types
Psalm provides utility types such as properties-of<T>, public-properties-of<T>, protected-properties-of<T>, private-properties-of<T>, class-string-map<T of Foo, T>, and variable templates. See PHPDoc Utility Types for details.
/**
* @template T of object
* @param class-string-map<T, T> $instances
* @param class-string<T> $class
* @return T
*/
function instance(array $instances, string $class): object
{
return $instances[$class];
}
PHPStan Utility Types
PHPStan supports template-type to extract a template type from an object argument, and new to create an object type from a class-string<T>.
/**
* @template T of object
* @param class-string<T> $class
* @return new<T>
*/
function create(string $class): object
{
return new $class();
}
Type Aliases
Psalm Type Aliases
/**
* @psalm-type UserId = positive-int
* @psalm-type UserData = array{id: UserId, name: non-empty-string}
*/
final class UserTypes {}
/**
* @psalm-import-type UserData from UserTypes
* @param UserData $user
*/
function saveUser(array $user): void {}
PHPStan Type Aliases
/**
* @phpstan-type UserId positive-int
* @phpstan-type UserData array{id: UserId, name: non-empty-string}
*/
final class UserTypes {}
/**
* @phpstan-import-type UserData from UserTypes
* @param UserData $user
*/
function saveUser(array $user): void {}
PHPStan also supports global type aliases in phpstan.neon.
parameters:
typeAliases:
UserId: positive-int
UserData: 'array{id: UserId, name: non-empty-string}'
Assert and Type Guards
Psalm Assertions
/**
* @psalm-assert non-empty-string $value
*/
function assertNonEmptyString(mixed $value): void
{
if (! is_string($value) || $value === '') {
throw new InvalidArgumentException();
}
}
/**
* @psalm-assert-if-true User $value
*/
function isUser(mixed $value): bool
{
return $value instanceof User;
}
Psalm also supports @psalm-if-this-is and @psalm-this-out for changing the inferred type of $this.
PHPStan Assertions
/**
* @phpstan-assert non-empty-string $value
*/
function assertNonEmptyString(mixed $value): void;
/**
* @phpstan-assert-if-true User $value
*/
function isUser(mixed $value): bool;
PHPStan supports @phpstan-self-out and @phpstan-this-out for changing the current object’s type after a method call.
/**
* @template TValue
*/
final class Bag
{
/**
* @template TItem
* @param TItem $item
* @phpstan-self-out self<TValue|TItem>
*/
public function add(mixed $item): void {}
}
By-Reference Output Types
Psalm and PHPStan support @param-out to describe the type of a by-reference parameter after a function call. Psalm also supports @psalm-param-out.
/**
* @param-out non-empty-string $value
*/
function fillDefault(?string &$value): void
{
$value ??= 'default';
}
Immutability and Side Effects
Psalm can describe purity, mutation, and immutability.
/**
* @psalm-immutable
*/
final class Point
{
public function __construct(
public float $x,
public float $y
) {}
/** @psalm-mutation-free */
public function move(float $x, float $y): self
{
return new self($this->x + $x, $this->y + $y);
}
}
Common Psalm tags:
| Tag | Meaning |
|---|---|
@psalm-pure |
Pure function depending only on its input |
@psalm-impure |
Function with side effects |
@psalm-mutation-free |
Method that mutates neither itself nor external state |
@psalm-external-mutation-free |
Method that does not mutate external state |
@psalm-immutable |
Immutable class |
@psalm-readonly / @readonly |
Read-only property |
@psalm-allow-private-mutation |
Property can be mutated only in private context |
PHPStan supports @phpstan-pure and @phpstan-impure. For readonly properties that can be changed privately, use @phpstan-allow-private-mutation or @phpstan-readonly-allow-private-mutation.
Security Analysis
Psalm taint analysis can express untrusted input sources, dangerous sinks, escaping, and flow.
/**
* @psalm-taint-source input
*/
function userInput(): string
{
return $_GET['q'] ?? '';
}
/**
* @psalm-taint-sink sql $query
*/
function query(string $query): void {}
/**
* @psalm-taint-escape sql
*/
function escapeSql(string $value): string
{
return addslashes($value);
}
Main taint tags:
| Tag | Use |
|---|---|
@psalm-taint-source <type> |
Untrusted input source |
@psalm-taint-sink <type> <param> |
Dangerous sink |
@psalm-taint-escape <type> |
Escaping or sanitizing operation |
@psalm-taint-unescape <type> |
Mark escaped data as tainted again |
@psalm-taint-specialize |
Specialize taint in functions or classes |
@psalm-flow (...) -> return |
Explicit taint flow |
Debugging and Checking Tags
/** @psalm-trace $value */
$value = userInput();
/** @psalm-check-type $value = non-empty-string */
$value = 'BEAR';
/** @psalm-check-type-exact $value = 'BEAR' */
$value = 'BEAR';
These tags are development aids for checking Psalm’s inferred types.
BEAR.Sunday Recommendations
- Use native PHP type declarations when they can express the type.
- Use PHPDoc for element types, array shapes, lists, generics, and type aliases.
- Prefer syntax that both Psalm and PHPStan understand for public APIs.
- Use
@psalm-*for Psalm-only analysis and@phpstan-*for PHPStan-only analysis. - When a shape becomes too large, consider a DTO, value object, or entity instead of a large array shape.
- For SQL, HTML, file paths, commands, and other security boundaries, combine
literal-string, taint annotations, and semantic types.
Compatibility Notes
| Item | Note |
|---|---|
int-range / int<min, max> |
Psalm uses int-range; PHPStan uses int<min, max>. |
@psalm-type / @phpstan-type |
Tool-specific. Define both when both analyzers need the alias. |
properties-of |
Psalm utility type. Use another expression for PHPStan. |
| Unsealed shapes | Use them when extra keys are intentional. |
literal-string |
Useful for security-sensitive strings such as SQL and template fragments. |
array<string, T> |
PHP may cast decimal string keys to int. Consider PHPStan’s non-decimal-int-string. |