Late Static Binding — PHP 5.3 Feature
debt(d5/e1/b3/t7)
Closest to 'specialist tool catches it' (d5). The detection_hints list phpstan and rector as the tools, both specialist static analysis tools. The code_pattern `new self()` is detectable by these tools but not by a default linter or compiler — it's syntactically valid and only wrong in certain inheritance contexts.
Closest to 'one-line patch or single-call swap' (e1). The quick_fix is explicit: replace `new self()` with `new static()` (or `self::$property` with `static::$property`). This is a direct single-call textual substitution, often automatable via rector.
Closest to 'localised tax' (b3). The misuse is confined to static factory methods or inheritable static properties in a class hierarchy. It doesn't bleed across the entire codebase — only the affected class and its subclasses pay the cost. Other components are unaffected.
Closest to 'serious trap' (t7). The misconception field states explicitly that developers believe `self::` and `static::` are interchangeable — but self:: always resolves to the defining class at compile time while static:: resolves at runtime to the called class. This contradicts how most developers reason about inheritance (expecting runtime polymorphism), and the 'obvious' usage of self:: in a base factory method silently produces the wrong type without any error.
TL;DR
Explanation
PHP 5.3 introduced static:: for Late Static Binding. self:: always resolves to the class where the method was written. static:: resolves to the class that was called at runtime. Classic use case: static factory methods in inheritance. class Base { static function create() { return new static(); } } — new self() always creates Base; new static() creates the called subclass. LSB is also used for static properties in hierarchies: static::$table in an ORM base class. get_class($this) is the instance equivalent for dynamic resolution.
Common Misconception
Why It Matters
Common Mistakes
- Using self:: in static factory methods — breaks when called on subclasses.
- Using static:: when self:: is intentionally needed (singleton base class).
- Confusing static:: with $this (instance context).
Code Examples
class Base {
public static function create(): static {
return new self(); // Always Base — broken in subclasses
}
}
class Child extends Base {}
Child::create(); // Returns Base, not Child!
class Base {
public static function create(): static {
return new static(); // Returns calling class
}
}
class Child extends Base {}
Child::create(); // Returns Child instance