Property Hooks (PHP 8.4)
debt(d5/e3/b3/t5)
Closest to 'specialist tool catches it' (d5), since phpstan/rector (per detection_hints.tools) can flag recursive hooks or boilerplate getter/setter patterns ripe for conversion, but the subtler bugs (set hook not assigning) need static analysis to catch.
Closest to 'simple parameterised fix' (e3), per quick_fix: replacing get/set boilerplate with hooks is a localised pattern swap within a class, occasionally requiring extraction of complex logic to private methods.
Closest to 'localised tax' (b3), since hooks apply per-class (applies_to value objects/entities) and don't propagate system-wide; callers use normal property syntax so the choice stays contained.
Closest to 'notable trap' (t5), per misconception and common_mistakes: recursive get hooks reading $this->name infinitely-loop, and set hooks that forget to assign silently drop values — documented gotchas devs learn the hard way.
Also Known As
TL;DR
Explanation
Property hooks allow get and set logic inline: public string $name { get => strtoupper($this->name); set => $this->name = trim($value); }. The get hook runs when the property is read; set runs when it is assigned. A get-only hook makes the property effectively read-only to external code. This eliminates entire classes of getter/setter boilerplate while keeping the clean property-access syntax. Works with constructor promotion, interfaces, and abstract classes.
Common Misconception
Why It Matters
Common Mistakes
- Using both a hook and a traditional getter/setter for the same property — they conflict.
- Recursive get hooks — a get hook that reads $this->name triggers itself infinitely; use $this->name directly inside the hook.
- Set hooks that don't assign — if the set hook doesn't assign to the backing value, the value is never stored.
- Not understanding that a get-only hook prevents direct assignment from outside the class.
Code Examples
// PHP 8.3 — boilerplate getters/setters:
class User {
private string $email;
public function getEmail(): string { return strtolower($this->email); }
public function setEmail(string $email): void {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) throw new \InvalidArgumentException('Invalid email');
$this->email = $email;
}
}
// PHP 8.4 — property hooks:
class User {
public string $email {
get => strtolower($this->email);
set {
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) {
throw new \InvalidArgumentException('Invalid email');
}
$this->email = $value;
}
}
// Usage: $user->email = 'Alice@EXAMPLE.COM'; echo $user->email; // alice@example.com
}