Nullable Types (?Type)
debt(d5/e3/b3/t5)
Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan, psalm, and rector — all specialist static analysis tools. Common misuse (e.g. missing ?Type annotation, or using mixed to hide nullability) won't surface at compile time or via default linters, but these dedicated tools catch it reliably when configured.
Closest to 'simple parameterised fix' (e3). The quick_fix describes adding or removing ?Type annotations on parameters and return types, which is a localised pattern-replacement. Rector can automate many cases, but the developer must also audit call sites and add null-checks or null-safe operator usage — slightly more than a one-liner but contained within one component.
Closest to 'localised tax' (b3). Nullable types apply across web, cli, and queue-worker contexts, but the burden is per-function/per-class boundary. Overuse of ?Type (a noted common mistake) adds review overhead in the affected component, but it doesn't reshape the entire codebase or impose a strong gravitational pull beyond the functions where it's declared.
Closest to 'notable trap' (t5). The misconception field identifies the canonical gotcha: developers often assume nullable types encourage passing null everywhere, when the intent is the opposite — they make nullability explicit and opt-in. Additional common mistakes (forgetting explicit return null, not using ?-> on nullable return values, overusing ?Type to avoid validation) are documented gotchas that most PHP developers eventually encounter, matching the t5 anchor.
Also Known As
TL;DR
Explanation
Nullable types (PHP 7.1+) prefix a type declaration with ? to indicate the value may be null in addition to the declared type. function find(?int $id): ?User accepts null for $id and may return a User or null. This is equivalent to the union type int|null and User|null introduced in PHP 8.0. Nullable types made it practical to type-hint optional parameters and nullable return values without resorting to mixed. Combine with the null coalescing operator (??) and nullsafe operator (?->) for clean null handling.
Common Misconception
Why It Matters
Common Mistakes
- Using ?Type when null should actually be forbidden — nullable types are sometimes overused to avoid validation.
- Forgetting that nullable return types require explicit return null statements — returning nothing returns null implicitly but ?Type requires it to be intentional.
- Not using the nullsafe operator (?->) when chaining calls on nullable return values.
- Returning null from constructors — constructors cannot be nullable; throw an exception instead.
Code Examples
// Nullable parameter used where null should be invalid:
function setAge(?int $age): void {
$this->age = $age; // Allows null — but a person must have an age
}
// If null means 'unknown', model it explicitly:
function setAge(int $age): void { // Enforce valid age
if ($age < 0 || $age > 150) throw new InvalidArgumentException();
$this->age = $age;
}
// ?Type is shorthand for Type|null
function findUser(int $id): ?User {
return $this->db->find($id); // null if not found
}
// Nullable parameter with default
function greet(?string $name = null): string {
return 'Hello, ' . ($name ?? 'Guest');
}
// Typed nullable property (PHP 7.4+)
class Order {
public ?\DateTimeImmutable $shippedAt = null; // not shipped yet
}
// PHP 8.0+ — use union type for explicit null
function process(int|null $value): void {}
// Equivalent to: function process(?int $value): void {}