Late Static Binding
Part of the Object Oriented Programming section of Coddy's PHP journey — lesson 36 of 91.
When you use self:: in a parent class, it always refers to the class where it's written, not the class that's actually being used. This can cause unexpected behavior in inheritance. Late static binding solves this problem using the static:: keyword.
Consider this example where self:: doesn't work as expected:
<?php
class ParentClass {
public static function create() {
return new self();
}
public static function getClassName() {
return self::class;
}
}
class ChildClass extends ParentClass {}
$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();
Output:
ParentClass
ParentClassEven though we called ChildClass::create(), we got a ParentClass object because self:: is resolved at compile time. Now let's use static:: instead:
<?php
class ParentClass {
public static function create() {
return new static();
}
public static function getClassName() {
return static::class;
}
}
class ChildClass extends ParentClass {}
$obj = ChildClass::create();
echo get_class($obj) . "\n";
echo ChildClass::getClassName();
Output:
ChildClass
ChildClassWith static::, PHP waits until runtime to determine which class to use. This is called "late" binding because the decision is delayed. It's essential for factory methods and any static method that should respect inheritance.
Key Point: Use static:: instead of self:: when you want child classes to properly inherit and customize static behavior.
Challenge
EasyLet's build a model registry system that demonstrates the power of late static binding. You'll create a base Model class with a factory method that child classes can inherit — and thanks to static::, each child class will correctly create instances of itself rather than the parent.
You'll organize your code across four files:
Model.php— Create a baseModelclass with a protected static property$tableNameset to"models". Add a static methodcreate()that usesnew static()to return a new instance of whatever class called it. Add another static methodgetTable()that returnsstatic::$tableName. Finally, add adescribe()method that returns"[ClassName] from table [tableName]"usingstatic::classandstatic::$tableName.User.php— Create aUserclass that extendsModel. Include the parent file. Override the static$tableNameproperty to"users". The class inherits all methods fromModelwithout any changes.Product.php— Create aProductclass that extendsModel. Include the parent file. Override the static$tableNameproperty to"products". Again, the class inherits all methods from the parent.main.php— Include both the User and Product files. Use the inheritedcreate()factory method to create instances of bothUserandProduct. For each instance, calldescribe()and print the result on its own line. Then print the table name for each class usingUser::getTable()andProduct::getTable(), each on its own line.
The magic here is that the create() and getTable() methods are defined only in the parent Model class, yet they correctly work with child classes because static:: delays the binding until runtime. When you call User::create(), PHP creates a User instance — not a Model instance — because static:: refers to the class that was actually called.
Your output should show four lines: the description for the User instance, the description for the Product instance, the User table name, and the Product table name.
Cheat sheet
The self:: keyword in a parent class always refers to the class where it's written, resolved at compile time. Late static binding with static:: resolves the reference at runtime, allowing child classes to properly inherit and customize static behavior.
Using self:: (compile-time binding)
<?php
class ParentClass {
public static function create() {
return new self(); // Always creates ParentClass
}
public static function getClassName() {
return self::class; // Always returns "ParentClass"
}
}
class ChildClass extends ParentClass {}
$obj = ChildClass::create(); // Returns ParentClass instance
echo ChildClass::getClassName(); // Outputs: ParentClass
Using static:: (runtime binding)
<?php
class ParentClass {
public static function create() {
return new static(); // Creates instance of calling class
}
public static function getClassName() {
return static::class; // Returns calling class name
}
}
class ChildClass extends ParentClass {}
$obj = ChildClass::create(); // Returns ChildClass instance
echo ChildClass::getClassName(); // Outputs: ChildClass
Key Point: Use static:: instead of self:: when you want child classes to properly inherit and customize static behavior. This is essential for factory methods and any static method that should respect inheritance.
Try it yourself
<?php
require_once 'User.php';
require_once 'Product.php';
// TODO: Use the inherited create() factory method to create a User instance
// TODO: Use the inherited create() factory method to create a Product instance
// TODO: Call describe() on the User instance and print the result
// TODO: Call describe() on the Product instance and print the result
// TODO: Print User::getTable()
// TODO: Print Product::getTable()
?>This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesIntroduction to OOPClasses vs ObjectsThe $this KeywordMethodsPropertiesConstructor (__construct)Destructor (__destruct)Recap - Simple Calculator4Inheritance
Basic InheritanceThe parent:: KeywordMethod OverridingThe final KeywordAbstract ClassesRecap - Employee Hierarchy7Encapsulation
Public, Protected, PrivateAccess Modifiers in DepthGetters and SettersInformation HidingConstructor Promotion (8.0)Recap - Student Records System2Namespaces & Autoloading
Introduction to NamespacesThe use KeywordPSR-4 Autoloading StandardComposer AutoloaderRecap - Organized Project5Interfaces & Contracts
Introduction to InterfacesImplementing InterfacesMultiple Interface ImplementInterface vs Abstract ClassType Hinting with InterfacesRecap - Shape Calculator8Magic Methods
Magic Methods Introduction__toString & __debugInfo__get, __set, __isset, __unset__call & __callStatic__clone & Object Cloning__serialize & __unserializeRecap - Custom Collection11Type System & Error Handling
Type DeclarationsNullable TypesUnion & Intersection TypesException ClassesCustom Exception HierarchyTry, Catch, FinallyRecap - Form Validator14Project: Library Management
Project OverviewBook and User Classes3Class Properties
Instance vs Static PropertiesConstants in ClassesStatic Methods & PropertiesPrivate & Protected PropertiesReadonly Properties (PHP 8.1)Recap - Bank Account Manager6Polymorphism
Method Overriding RevisitedPolymorphism via InterfacesType Hinting & Union TypesLate Static BindingRecap - Payment Processor9Traits
Introduction to TraitsUsing Multiple TraitsTrait Conflict ResolutionAbstract Methods in TraitsTraits vs Inheritance12Design Patterns Part 1
Intro to Design PatternsSingleton PatternFactory PatternObserver PatternStrategy Pattern