In PHP, what is the difference between self:: and static:: inside a parent class method, and what is late static binding?
answer
- defining class vs called class
- self:: behaves like __CLASS__
- resolved at runtime from the last call
- self:: and parent:: forward the called class
- get_called_class() and static::class
basics
~20 sself:: always means the class where the method is written; static:: means the class the call was actually made on, resolved at runtime. That runtime lookup is late static binding, and it lets a parent's method reach a child's override.
solid answer
~40 s`self::` is bound to the class that **defines** the method, exactly like `__CLASS__`. `static::` is bound to the **called class**: the class named in the last non-forwarding call (`B::test()` makes it `B`), or the object's class when the method runs on an instance. That runtime resolution is **late static binding**. So if `A::test()` calls `self::who()` and `B` overrides `who()`, `B::test()` still runs `A::who()`; with `static::who()` it runs `B::who()`. Calls through `self::`, `parent::` and `static::` *forward* the called class, while an explicit `A::foo()` resets it to `A`. `static::` works for methods, static properties, constants, `new static` and `static::class`, and `get_called_class()` returns the same name as a string.
code
php · 18 lines<?php
class A
{
public static function who(): string { return 'A'; }
public static function viaSelf(): string { return self::who(); }
public static function viaStatic(): string { return static::who(); }
}
class B extends A
{
public static function who(): string { return 'B'; }
}
echo B::viaSelf(), PHP_EOL; // A
echo B::viaStatic(), PHP_EOL; // B
echo get_class(new B()), ' ', B::class, PHP_EOL; // B Bgo deeper
Remember that self:: means the class where the code is written and static:: means the class the call was made on.
Trace a snippet through forwarding calls and explain why an explicit ClassName:: call resets the called class while self:: and parent:: do not.
Decide per member whether subclasses should be able to change it, and catch the private-method trap where static:: finds a subclass's private override.
Set a convention for base classes that are meant to be extended: which extension points use static::, and which invariants stay on self:: or become final.
## Two ways to say "this class" Inside a class body PHP gives you two keywords that both look like "the current class": - **`self`** refers to the class in which the code is **written**. It is fixed when the method is compiled, the same way `__CLASS__` is. - **`static`** refers to the **called class**, the class the call was actually made through. It is looked up at runtime. The difference only shows when a method is inherited. A method defined in `A` and invoked as `B::test()` has `self` = `A` but `static` = `B`. ## Late static binding The manual names the runtime lookup behind `static::` **late static binding** (LSB). "Late" because it is computed from runtime information rather than from where the method is defined; "static" because it was designed for static calls, although it works in instance methods too. PHP stores the class named in the **last non-forwarding call**: - For a static call such as `B::test()`, the called class is the class written to the left of `::`, here `B`. - For an instance call such as `$b->test()`, the called class is the class of the object. `static::` then resolves to that stored class. ## Forwarding and non-forwarding calls Not every `::` call resets the called class. Calls introduced by `self::`, `parent::` and `static::` are **forwarding** calls: they keep the called class of the caller. A call that names a class explicitly, such as `A::foo()`, is **non-forwarding** and makes `A` the called class. `forward_static_call()` exists for the rare case where you need a forwarding call through a callable. | Call written inside `B::test()`, invoked as `C::test()` | Called class inside `A::foo()` | |---|---| | `A::foo()` | `A` (explicit name resets it) | | `parent::foo()` | `C` (forwarded) | | `self::foo()` | `C` (forwarded) | This is the manual's own example, with `C extends B extends A`, and it prints `A`, `C`, `C`. ## What `static` can be used for Late static binding is not limited to method calls. Inside a class, `static` can appear as: 1. `static::method()`: call the called class's version of a static method. 2. `static::$prop`: read the called class's static property (only static properties; there is no `static::` form for instance properties). 3. `static::CONSTANT`: read the called class's constant, so a subclass can override a value the parent's logic uses. 4. `new static()`: instantiate the called class. 5. `static::class`: the called class name as a string, the same value `get_called_class()` returns. 6. `: static` as a return type (PHP 8.0 and later): the method returns an instance of the called class. `static::` cannot be used in a compile-time constant expression; `const X = static::Y;` is a compile error, because constant expressions are evaluated without a called class. ## A trap with private methods In an instance method, `$this->foo()` and `static::foo()` can behave differently. `$this->foo()` called from `A`'s code looks for a private `A::foo()` first, because private methods are resolved in the calling scope. `static::foo()` looks up `foo()` in the called class. If a subclass `C` declares its own **private** `foo()`, `static::foo()` from `A`'s code finds `C::foo()` and fails with `Call to private method C::foo() from scope A`. If the subclass does not redeclare it, the inherited private method still has scope `A` and the call succeeds. ## When to choose which - Use **`self::`** when you mean exactly this class's member and do not want subclasses to change the behaviour, for example a private helper or a constant the parent's invariant depends on. - Use **`static::`** when the parent defines an algorithm and subclasses supply parts of it: a table name, a factory, a configuration constant. - For code written in a `final` class the two resolve to the same class, so the choice only documents intent. A useful review habit: every `static::` in a base class is an **extension point**, a place where a subclass can change the base class's behaviour. Every `self::` is a **fixed reference**. Reading a class with that lens tells you which members subclasses are expected to override, and a `self::` that should have been `static::` (or the reverse) usually shows up as a subclass override that is silently ignored. Interviewers often pair this with a tiny snippet and ask for the output. Trace it by asking two questions: where is the method **written** (that answers `self`), and which class was the call **made on** after following forwarding calls (that answers `static`).
- In PHP, inside B::test(), which class does static:: resolve to after the calls A::foo(), parent::foo() and self::foo(), if test() was invoked as C::test()?`A::foo()` names a class explicitly, so it is non-forwarding and `static` inside `foo()` is `A`. `parent::foo()` and `self::foo()` are forwarding calls, so they keep the original called class and `static` is `C` in both.
- In PHP, what does static:: resolve to inside an instance method called as $obj->run()?The class of `$obj`. For an instance call the called class is the runtime class of the object, so `static::LABEL` or `static::helper()` pick the object's class's version even when `run()` is defined in a parent.
- In PHP, can a class constant be defined as const FULL = static::PREFIX . 'x'?No. Constant expressions are compiled without a called class, so PHP rejects it with a compile error saying `static::` is not allowed in compile-time constants. Use `self::PREFIX`, or compute the value in a static method that reads `static::PREFIX`.
self:: is a letterhead printed with the office that drafted the template; static:: is a blank line on the same template that each branch office fills in with its own name when it sends the letter.
saying these in an interview costs you the question
- Says self:: and static:: always resolve to the same class
- Thinks self:: refers to the class of $this
- Believes parent:: resets the called class to the parent
- Claims static:: only works for static methods, not constants or new
- Describes late static binding as a performance optimisation