Magic Methods (__get, __set, __call…)
debt(d5/e5/b5/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan and psalm — both specialist static analysis tools — as the means to detect __get/__set hiding class properties. A default linter won't catch the misuse; it requires running phpstan or psalm with appropriate strictness levels to surface the opacity introduced by magic methods.
Closest to 'touches multiple files / significant refactor in one component' (e5). The quick_fix suggests avoiding __get/__set entirely and replacing them with explicit typed properties. Removing magic methods from a class requires identifying all callers relying on dynamic property access, adding explicit properties, and updating type hints — a non-trivial refactor within or across a component, but not a full architectural rework.
Closest to 'persistent productivity tax' (b5). Magic methods apply across web, cli, and queue-worker contexts and carry a persistent tax: IDEs lose autocomplete, static analysis tools lose visibility, and every future maintainer must mentally model the hidden dynamic behaviour. It slows many work streams but doesn't necessarily define the entire system's shape.
Closest to 'serious trap — contradicts how a similar concept works elsewhere' (t7). The misconception field explicitly captures the trap: developers believe __get/__set provide good flexible property access, but the 'obvious' usage actively harms discoverability, type safety, and tooling. Additionally, __toString() throwing exceptions silently fails in PHP < 8, and __clone() not deep-copying is a well-known gotcha that contradicts expectations from other OO languages. Multiple serious behavioural surprises elevate this above t5.
Also Known As
TL;DR
Explanation
PHP magic methods include __construct/__destruct (lifecycle), __get/__set/__isset/__unset (property overloading), __call/__callStatic (method overloading), __toString (string casting), __invoke (callable objects), __clone (copy construction), and __wakeup/__sleep/__serialize/__unserialize (serialisation). Magic methods can dramatically simplify APIs but also hide bugs, complicate static analysis, and create security risks — __toString XSS, __wakeup object injection. Use them intentionally and document their behaviour clearly.
Common Misconception
Why It Matters
Common Mistakes
- __get() and __set() that silently create properties instead of throwing on undefined access — masks typos.
- __toString() that throws an exception — PHP does not allow exceptions from __toString() prior to PHP 8.
- __clone() not deep-copying nested objects — the cloned object shares references with the original.
- Heavy logic in __construct() — makes the class hard to instantiate in tests and violates single responsibility.
Code Examples
// __get silently swallows undefined property access:
class Config {
public function __get(string $key): mixed {
return $this->data[$key] ?? null; // Returns null for any typo — no error
}
}
$config->databse_host; // Typo — silently returns null instead of error
class Collection {
private array $items = [];
public function __construct(array $items = []) {
$this->items = $items;
}
// Called when accessing inaccessible/undefined property
public function __get(string $name): mixed {
return $this->items[$name] ?? null;
}
// Called when setting inaccessible/undefined property
public function __set(string $name, mixed $value): void {
$this->items[$name] = $value;
}
// Called by echo / string cast
public function __toString(): string {
return implode(', ', $this->items);
}
// Called by var_dump — PHP 8.2+
public function __debugInfo(): array {
return ['count' => count($this->items)];
}
// Called when object used as function
public function __invoke(mixed $item): static {
return new static([...$this->items, $item]);
}
}