← Back to blog

Late Static Binding Explained

| PHP

This article was published over 2 years ago. Some information may be outdated.

PHP 5.3 introduced an OO feature called Late Static Binding. The name sounds complex, but the concept is simple.

If you look at modern PHP frameworks and libraries, you will see static methods used extensively throughout their classes.

This post covers the differences between self and static and when to use each one.

String Class

Imagine you are creating a composer library that provides string helpers. One of the helpers is a string decorator:

namespace Mayahi\MyStringPackage;

class Str
{
    public function decorate(string $str): string
    {
        $lines = self::getLines();

        return $lines . PHP_EOL . $str . PHP_EOL . $lines;
    }

    public function getLines()
    {
        return str_repeat('-', 10);
    }

        // other helpers ...
}

Try it out:

$str = new Mayahi\MyStringPackage\Str();
echo $str->decorate('Hello World');

Output:

----------
Hello World
----------

What if you want to change the getLines() method to generate twenty stars per line (*) instead of ten dashes (-)?

Since the Str class relies on getLines(), the only way to accomplish that is to create a subclass that extends Str and overrides getLines():

class MyStr extends Str
{
    public function getLines()
    {
        return str_repeat('*', 20);
    }
}

Try it out:

$str = new MyStr();
echo $str->decorate('Hello World');

Output:

----------
Hello World
----------

It still returns the same result. The reason is the self pseudo-variable.

The self pseudo-variable refers to the class where it is defined. This means getLines() is always resolved to the parent class, regardless of subclassing.

To solve this, change self to static in the decorate() method:

public function decorate(string $str): string
{
    $lines = static::getLines();
    return $lines.PHP_EOL.$str.PHP_EOL.$lines;
}

Run your code again:

********************
Hello World
********************

The result is correct now.

By using Late Static Binding (static), you are telling PHP to look for the getLines() method in the subclass first. If PHP does not find it there, it falls back to the current class.

A Real-World Example

Look at Laravel's Str class. Late Static Binding is used in many methods, such as containsAll() which uses it to call the contains method:

if (! static::contains($haystack, $needle)) {
     return false;
}

This allows developers to extend the Str class and provide their own implementation for specific methods.

When to use it?

If you are creating an extendable class, or you are relying on subclasses to provide or override specific methods, use static.

Remember that self refers to the current class, not the object. You must use $this to refer to the current object, such as non-static properties.

Real-World Example: Singleton Design Pattern

The Singleton Design Pattern ensures that a class is only instantiated once during runtime.

Consider a DatabaseConnection class that establishes a MySQL connection using PDO:

class DatabaseConnection
{
    private $dbh;

    public function __construct(string $dsn, string $user, string $password)
    {
        // Connect
    }

    public function fetch(string $sql, array $bindings): ?array
    {
        // Fetch some data
    }

    // Rest implemention...

}

If you call the database connection in different places, you end up establishing multiple database connections:

$db1 = new DatabaseConnection('mysql:dbname=testdb;host=127.0.0.1', 'user', 'password');
echo 'Object Id: '.spl_object_id($db1).PHP_EOL;

$db2 = new DatabaseConnection('mysql:dbname=testdb;host=127.0.0.1', 'user', 'password');
echo 'Object Id: '.spl_object_id($db2).PHP_EOL;
Object Id: 1
Object Id: 2

The spl_object_id returns the unique identifier for the given object during runtime.

Since DatabaseConnection is instantiated twice, spl_object_id returns two different values, confirming two separate database connections.

The Singleton Design Pattern ensures the object is only instantiated once during runtime, limiting the database connections to one:

class Singleton
{
    private static ?object $instance = null;

    protected function __construct() { }

    protected function __clone() { }

    public function __wakeup() { }

    public static function getInstance()
    {
        $subclass = static::class;
        if (is_null(self::$instance)) {
            self::$instance = new static;
        }

        return self::$instance;
    }
}

Try it out:

class DatabaseConnection extends Singleton
{
    private $dbh;

    protected function __construct()
    {
        // Connect to the database
    }

    public function fetch(string $sql, array $bindings = []): ?array
    {
        // Fetch some data
    }

    // Rest implemention...

}

$db1 = DatabaseConnection::getInstance();
echo 'Object Id: '.spl_object_id($db1).PHP_EOL;

$db2 = DatabaseConnection::getInstance();
echo 'Object Id: '.spl_object_id($db2).PHP_EOL;
Object Id: 1
Object Id: 1

No matter how many times you call getInstance(), you get the exact same object.

Summary

  • self vs static -- self always resolves to the class where it is defined, while static resolves to the class that called the method at runtime.
  • Late Static Binding -- use static when you want subclasses to be able to override methods that are called internally by the parent class.
  • Extendable libraries -- if you are building a library meant to be extended, static is the correct choice; self will silently ignore overrides in subclasses.
  • Singleton pattern -- uses new static to ensure the correct subclass is instantiated, which is Late Static Binding in action.
Share