Menu
Coddy logo textTech

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
ParentClass

Even 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
ChildClass

With 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 icon

Challenge

Easy

Let'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 base Model class with a protected static property $tableName set to "models". Add a static method create() that uses new static() to return a new instance of whatever class called it. Add another static method getTable() that returns static::$tableName. Finally, add a describe() method that returns "[ClassName] from table [tableName]" using static::class and static::$tableName.
  • User.php — Create a User class that extends Model. Include the parent file. Override the static $tableName property to "users". The class inherits all methods from Model without any changes.
  • Product.php — Create a Product class that extends Model. Include the parent file. Override the static $tableName property to "products". Again, the class inherits all methods from the parent.
  • main.php — Include both the User and Product files. Use the inherited create() factory method to create instances of both User and Product. For each instance, call describe() and print the result on its own line. Then print the table name for each class using User::getTable() and Product::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()

?>
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming