π§ PHP Late Static Binding Explained: self:: vs static:: vs new static() for Real-World Use
Hey fellow devs,
Ever found yourself confused by the difference between self::
, static::
, new self()
and new static()
in PHP?
I did too. Until I slowed down, played with examples, and finally understood the beauty of something called Late Static Binding (LSB).
Letβs break it down like weβre talking over a project debug session.
π€ Whatβs the Problem?
You have a parent class that defines a static method. You extend it with a child class and call the method from the child. But surprisingly, the method behaves like itβs still running in the parent.
Like this:
class ParentClass {
public static function whoAmI() {
echo "I am " . self::class;
}
}
class ChildClass extends ParentClass {}
ChildClass::whoAmI(); // β Output: I am ParentClass
Why did it say ParentClass
? You called it from ChildClass
, right?
Because self::
refers to the class where the method is defined, not who called it.
And that's the limitation Late Static Binding solves.
π Enter: static::
β the Fix
class ParentClass {
public static function whoAmI() {
echo "I am " . static::class;
}
}
class ChildClass extends ParentClass {}
ChildClass::whoAmI(); // β
Output: I am ChildClass
Thatβs it! With static::
, PHP waits until runtime to figure out which class is calling. Hence the term late binding.
π§ͺ self::class
vs static::class
Keyword | What it returns | When it's resolved |
---|---|---|
self::class |
The class where it's written | At compile time |
static::class |
The class that called it | At runtime β |
This is especially powerful in patterns like factories or service locators.
π Object Creation: new self()
vs new static()
Letβs create objects and see the difference:
class Animal {
public static function create() {
return new static(); // π creates the calling class
}
}
class Dog extends Animal {}
class Cat extends Animal {}
$dog = Dog::create();
$cat = Cat::create();
echo get_class($dog); // Dog β
echo get_class($cat); // Cat β
-
new static()
β respects the child class -
new self()
β would have returnedAnimal
in both cases β
So again...
Expression | What it does |
---|---|
new self() |
Create object of defined class |
new static() |
Create object of calling class β |
π Real Use Case (Like in a Plugin)
Imagine you're building a WordPress plugin with multiple shortcode handlers. You could create a BaseShortcode
like this:
class BaseShortcode {
public static function init() {
echo "Booting: " . static::class . "\n";
return new static(); // π―
}
}
class GalleryShortcode extends BaseShortcode {}
class FormShortcode extends BaseShortcode {}
$g = GalleryShortcode::init(); // GalleryShortcode
$f = FormShortcode::init(); // FormShortcode
If you had used new self()
and self::class
, both would have printed BaseShortcode
. Not what you want when building reusable code, right?
π§ Think of It Like This
-
self::
/new self()
β fixed, bound to the class that wrote the method -
static::
/new static()
β flexible, bound to the class that called the method
Late static binding gives your OOP code dynamic power while keeping it clean.
π§ͺ Practice Time
Can you make this code output:
Creating UserController
π Try replacing whatβs needed:
class BaseController {
public static function load() {
echo "Creating " . ???;
return ???;
}
}
class UserController extends BaseController {}
$ctrl = UserController::load();
(Answer: replace ???
with static::class
and new static()
π)
π§΅ Final Thoughts
Late static binding might sound fancy, but it's just a tool that lets your code act more like real-world behavior: flexible, extendable, and smart at runtime.
When youβre building reusable components, plugin frameworks, or Laravel-style factories β understanding this makes your OOP code next level.
Top comments (0)