Missing Return Type Declarations
debt(d5/e3/b3/t5)
Closest to 'specialist tool catches' (d5) — PHPStan/Psalm/Rector flag missing return types at appropriate levels; the PHP engine itself won't error on a missing declaration.
Closest to 'simple parameterised fix' (e3) — quick_fix says add return types method by method; each is a small annotation but multiple methods across files need touching, with possible cascading nullable/void adjustments.
Closest to 'localised tax' (b3) — applies broadly across web/cli/queue contexts but each missing type is a localised quality issue rather than a system-shaping choice; cumulatively slows analysis confidence.
Closest to 'notable trap' (t5) — the misconception that @return PHPDoc equals a native return type is a well-known gotcha; devs eventually learn PHPDoc isn't engine-enforced.
Also Known As
TL;DR
Explanation
PHP 7.0+ supports return type declarations. PHP 8.0+ adds union types and mixed. PHP 8.1 adds never (functions that always throw or exit). PHP 8.2 adds true/false as standalone types. Declaring return types enables PHPStan and Psalm to catch type mismatches, enables IDE autocompletion, serves as executable documentation, and allows the PHP engine to optimise. In PHP 8.0+ declare strict_types=1 and always annotate return types — there is no valid reason to omit them.
Common Misconception
Why It Matters
Common Mistakes
- Using @return PHPDoc instead of a native return type — PHPDoc is advisory only.
- Omitting return types on private methods — they benefit just as much from type declarations.
- Returning null from a non-nullable return type — add ?Type to acknowledge the nullable case.
- Not using void for functions that return nothing — void is an explicit contract.
Code Examples
// No return types — type errors caught at runtime, not analysis time:
function getUser($id) {
return $this->db->find($id); // What does this return? array? object? null?
}
function saveUser($data) {
$this->db->insert($data);
// Returns null implicitly — was that intentional?
}
// Explicit return types — analysable, self-documenting:
function getUser(int $id): ?User {
return $this->db->find($id); // Nullable User — clear contract
}
function saveUser(array $data): void {
$this->db->insert($data); // void = no return value — intentional
}
function findOrFail(int $id): User {
return $this->db->findOrFail($id); // Non-nullable — throws if not found
}