Monday, April 12, 2010

The dangers of Late Static Bindings

There's a lot of (justified) excitement about PHP 5.3 new features, such as the support of namespaces and anonymous functions. Though, some glittering capabilities of the language are definitely not gold: the goto statement is probably the most debated example, but also the long-awaited Late Static Bindings support is an hammer which may hurt your fingers...

Technically speaking, Late Static Bindings give you the possibility to implement an effective class hierarchy with static classes. You can already see that there are two dangerous words in this definition: static and hierarchy.
In the code sample, you can see that the pre-PHP 5.3 static methods resolution was not affected by subclassing, as self always targeted (and will continue to target) the original class it was written in. Therefore, the static keyword has been reused in the context of method calls, resolving to the class that makes the static call.
It's simpler to show it than to describe it:
<?php
class Base
{
    public static function getHelloText()
    {
        return 'Hello from Base!';
    }

    /**
     * 'self' is always resolved to the class it is
     * written in.
     */
    public static function helloWorld()
    {
        echo self::getHelloText(), "\n";
    }
}

class Subclass extends Base
{
    public static function getHelloText()
    {
        return 'Hello from Subclass!';
    }

    // helloWorld() is inherited
}

Subclass::helloWorld();
With Late Static Bindings:
<?php
class Base
{
    public static function getHelloText()
    {
        return 'Hello from Base!';
    }

    /**
     * 'self' is resolved at runtime (late binding)
     * to the class where the method is called.
     */
    public static function helloWorld()
    {
        echo static::getHelloText(), "\n";
    }
}

class Subclass extends Base
{
    public static function getHelloText()
    {
        return 'Hello from Subclass!';
    }

    // helloWorld() is inherited
}

Subclass::helloWorld();
An equivalent functionality is available with the method get_called_class(), which returns at runtime the name of the class a static method is called in. This was not possible at all before PHP 5.3 and discovering what class is called in a static method is the central point of LSB.

It's not a mistery that static methods should be used sparingly and for particular use cases, as they are a procedural solution. Moreover, inheritance is an overrated method of code reuse that favors implicit assumptions and exposures over explicit decisions. In this article we will compare two examples where Late Static Bindings support proves useful and where it is not.

Good example: abstract implementation of a Factory Method.
Let's suppose we have a small, two-level hierarchy of classes that emulate an enumerated type. If we have a static factory method on the base class, we can implement as Flyweights the whole hierarchy of objects with only one method:
<?php
/**
 * Every subclass will have a method getInstance()
 * that returns the singleton.
 */
class AbstractEnum
{
    private static $_instances = array();

    public static function getInstance()
    {
        $class = get_called_class();
        if (!isset(self::$_instances[$class])) {
            self::$_instances[$class] = new $class();
        }
        return self::$_instances[$class];
    }
}

class Subclass extends AbstractEnum {}

$subclass = Subclass::getInstance();
$otherSubclass = Subclass::getInstance();
var_dump($subclass === $otherSubclass);
This is only syntactic sugar for an hypothetical Base::getInstance('Class'), and the single objects must be mere ValueObjects or data container and have no behavior to mock out, since static methods are the death of testability.

Bad example: Active Record which does not evolve towards Repository
Suppose instead we have the classic ActiveRecord class we extend to produce a very simple object-relational mapper (most of the PHP ORMs are based on this pattern.) If we had a find() static method on the ActiveRecord class, with LSB we can now make it work on every subclass, so that to find our users we can call User::find($id).
This is the death of testability and good design however: how can we mock out this class from a service that uses it? We are stuck with a procedural, hardcoded User::find($id) call.
Instead, libraries such as Doctrine 2 has evolved towards the Repository pattern, extracting and managing a Repository object specific for User instances, which can be obtained and which we can call find() on. Moreover, we can inject it into any services.

What can we learn from these two examples? Static methods are often the sign of a missing concept in the design, something which would encapsulate a collection of objects of the same class, or their shared metadata. Introducing a new object, being it a Table Data Gateway or a Repository, makes this concept explicit and promotes it to a first-class object, which you can pass around and mock as you want.

2 comments:

  1. After 5 years, we can say that your concerns were completely wrong, since the late static binding is the foundation of Eloquent Orm, and many other cool things that emerged in the php world in the last years. Late static binding is something so normal in many language, and still there were people concerned about it in the php community

    ReplyDelete
  2. Carlo, maybe you want to give an example of those eloquent ORM?

    ReplyDelete