Late Static Bindings

As of PHP 5.3.0, PHP implements a feature called late static bindings which
can be used to reference the called class in a context of static inheritance.

More precisely, late static bindings work by storing the class named in the
last "non-forwarding call". In case of static method calls, this is the
class explicitly named (usually the one on the left of the
::
operator); in case of non static method calls, it is the class of the object. A
"forwarding call" is a static one that is introduced by self::,
parent::, static::, or, if going
up in the class hierarchy, forward_static_call().
The function get_called_class() can be used to retrieve
a string with the name of the called class and static::
introduces its scope.

This feature was named "late static bindings" with an internal perspective in
mind. "Late binding" comes from the fact that static::
will not be resolved using the class where the method is defined but it will
rather be computed using runtime information.
It was also called a "static binding" as it can be used for (but is not
limited to) static method calls.

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:

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.

In non-static contexts, the called class will be the class of the object
instance. Since $this-> will try to call private
methods from the same scope, using static:: may give
different results. Another difference is that static::
can only refer to static properties.

Late static bindings' resolution will stop at a fully resolved static call
with no fallback. On the other hand, static calls using keywords like
parent:: or self:: will forward the
calling information.

I think this will be pretty helpful too.My question is, can just 'static' by itself resolve to the late static class?I ask this because it could help in making new instances of the derived class, from a base class, by calling a derived class's static method instead of having to create a new instance of the derived class - or explicitly defining a 'getClass' method for each derived class.Example:<?php//There isn't really any purpose for this example I posted//Just a random implementationclass Base { static function useful() {//Create a list of instances of the derived class$list=array(); for ($i=0;$i<10;$i++) $list[]=new static(); //Here's the point in questionreturn $list; }}class Derived extends Base { static function somethingElse() {//...$list=static::useful(); }}?>I'm not sure what kind of lexical / whatever-it's-called problems this would make with parsing. I don't think it could really collide with any contexts where you would use static otherwise - variable / method declaration.

I don't think there should be a massive bloat in the PHP core to support all of this, but it would be nice to take advantage of the dynamic nature of PHP.

And yet another side note:If you're in the instance-level scope in a method of a base, and you want to get a top-level static, here's an ugly workaround (from Thacmus /lib/core.php - see SVN repo):<?php//Get reference [?] to static from class //$class - Class name OR object (uses get_class()) //$var - Not gonna sayfunction& get_static($class,$var) { //'static_get'?if (!is_string($class)) $class=get_class($class); if (!@property_exists($class,$var)) {trigger_error("Static property does not exist: $class::\$$var");//debug_callstack(); //This is just a wrapper for debug_backtrace() for HTMLreturn null; }//Store a reference so that the base data can be referred to //The code [[ return eval('return &'.$class.'::$'.$var.';') ]] does not work - can not return references... //To establish the reference, use [[ $ref=&get_static(...) ]]eval('$temp=&'.$class.'::$'.$var.';'); //usingreturn $temp;}?>

I discovered an interesting thing. The class name string must be accessed directly from "flat" variable. Late static binding code that get's it's variable from array that is passed by class instance, throws an syntax error. Bug?

// Returns the real name of the class calling the method, not the one in which it was declared.protected static function getStatic() {// If already storedif (self::$nextStatic) {// Clean and return$class = self::$nextStatic;self::$nextStatic = false; return $class; }

There are two issues with this workaround :- if you call a static method from global env, you need to declare the name of the class BEFORE calling the method, otherwise the workaround won't work (see 3rd and 4th examples). But I assume good programming makes few calls to static methods from global scope, so this shouldn't be long to fix if you use it.- the workaround fails to access to private or protected static vars, as it uses get_class_vars(). If you find any better solution, let us know.

With Php 5.3.0, upgrading will be easy : just delete the methods from the basic class, and search/replace any call to getStatic() and setNextStatic() by static:: - or one could use a selector on PHP_VERSION value to include either the BaseClass file with workaround or a BaseClass file using static::

Simple basic class which uses to get_called_class() to create singleton instances. A previous post by php at mikebird dot co dot uk explain how to do this, but the extended static variables require you to define them in child classes before they work.

<?php

abstract class Singleton { private static $instances = array();

public function __construct() {$class = get_called_class(); if (array_key_exists($class, self::$instances))trigger_error("Tried to construct a second instance of class \"$class\"", E_USER_WARNING); }

Using these magic function as described above will help you to find classes that try to access an undefined (and undocumented) class-member. In most cases: this is an error based on misspelled member names.