Late Static Bindings

Posted on March 27th, 2009 by Sam

PHP 5.3 implements a feature called “late static bindings” that has been in the works since the PHP Developers Meeting that took place in November 2005 in Paris. This feature resolves an issue of inheritance, used to reference the called class in a context of static inheritance. Prior to version 5.3 a problem existed if the class was extended and a call was from the child class, “late static binding” alleviates this issue. The name is in reverence to the internal process, late bindings are computed using runtime information; as opposed to being resolved using the class where the method was defined.

Limitations of self::

Static references to the current class like self:: or __CLASS__ are resolved using the class in which the function belongs, as in where it was defined; while static:: is resolved as to what class was called.

<?php
class A {
  public static function who() { echo __CLASS__; }
  public static function test() { self::who(); }
}
 
class B extends A {
  public static function who() { echo __CLASS__; }
}
 
B::test(); // A
?>

Late Static Bindings’ usage

Late static bindings tries to solve that limitation by introducing a keyword that references the class that was initially called at runtime. Basically, a keyword that would allow you to reference B from test() in the previous example. It was decided not to introduce a new keyword but rather use static that was already reserved.

<?php
class A {
  public static function who() { echo __CLASS__; }
  public static function test() { static::who(); } // Late Static Bindings
}
 
class B extends A {
  public static function who() { echo __CLASS__; }
}
 
B::test(); // B
?>
Note: static:: does not work like $this for static methods! $this-> follows the rules of inheritance while static:: doesn’t.

static:: usage in a non-static context

<?php
class A {
  public function __construct() { static::who(); }
  public static function who() { echo __CLASS__."n"; }
}
 
class B extends A { public function __construct() { static::who(); }
  public function test() { $o = new A(); }
  public static function who() { echo __CLASS__."n"; }
}
 
$o = new B;
$o->test();
// B
// A
?>
Note: Late static bindings’ resolution will stop at a fully resolved static call with no fall back. On the other hand, static calls using keywords like parent:: or self:: will forward the calling information.

Forwarding and non-forwarding calls

<?php
class A {
  public static function foo() { static::who(); }
  public static function who() { echo __CLASS__."n"; }
}
 
class B extends A {
  public static function test() {
    A::foo();
    parent::foo();
    self::foo();
  }
 
  public static function who() { echo __CLASS__."n"; }
}
 
class C extends B {
  public static function who() { echo __CLASS__."n"; }
}
 
C::test();
// A
// C
// C
?>

Edge cases

There are lots of different ways to trigger a method call in PHP, like callbacks or magic methods. As late static bindings base their resolution on runtime information, it might give unexpected results in so-called edge cases.

<?php
class A {
  protected static function who() { echo __CLASS__."n"; }
  public function __get($var) { return static::who(); }
}
 
class B extends A {
  protected static function who() { echo __CLASS__."n"; }
}
 
$b = new B;
$b->foo; // B
?>

You can find more information on the PHP manual page for Late Static Bindings.

Leave a Reply