PHP
downloads | documentation | faq | getting help | mailing lists | reporting bugs | php.net sites | links | conferences | my php.net

search for in the

Objects and references> <Type Hinting
Last updated: Fri, 10 Oct 2008

view this page in

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.

This feature was named "late static bindings" with an internal perspective in mind. "Late binding" comes from the fact that static:: will no longer 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:

Example #1 self:: usage

<?php
class {
    public static function 
who() {
        echo 
__CLASS__;
    }
    public static function 
test() {
        
self::who();      
    }  
}  

class 
extends {      
    public static function 
who() {
         echo 
__CLASS__;
    }  
}   

B::test();
?>

The above example will output:

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.

Example #2 static:: simple usage

<?php
class {
    public static function 
who() {
        echo 
__CLASS__;
    }
    public static function 
test() {
        static::
who(); // Here comes Late Static Bindings     
    
}  
}  

class 
extends {      
    public static function 
who() {
         echo 
__CLASS__;
    }  
}   

B::test();
?>

The above example will output:

B

Note: static:: does not work like $this for static methods! $this-> follows the rules of inheritance while static:: doesn't. This difference is detailed later on this manual page.

Example #3 static:: usage in a non-static context

<?php
class TestChild extends TestParent {
    public function 
__construct() {
        static::
who();
    }

    public function 
test() {
        
$o = new TestParent();
    }

    public static function 
who() {
        echo 
__CLASS__."\n";
    }
}

class 
TestParent {
    public function 
__construct() {
        static::
who();
    }

    public static function 
who() {
        echo 
__CLASS__."\n";
    }
}
$o = new TestChild;
$o->test();

?>

The above example will output:

TestChild
TestParent

Note: 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.

Example #4 Forwarding and non-forwarding calls

<?php
class {
    public static function 
foo() {
        static::
who();
    }
        
    public static function 
who() {
        echo 
__CLASS__."\n";
    }
}

class 
extends {
    public static function 
test() {
        
A::foo();
        
parent::foo();
        
self::foo();
    }

    public static function 
who() {
        echo 
__CLASS__."\n";
    }
}
class 
extends {
    public static function 
who() {
        echo 
__CLASS__."\n";
    }
}

C::test();
?>

The above example will output:

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.

Example #5 Late static bindings inside magic methods

<?php
class {

   protected static function 
who() {
        echo 
__CLASS__."\n";
   }

   public function 
__get($var) {
       return static::
who();
   }
}

class 
extends {

   protected static function 
who() {
        echo 
__CLASS__."\n";
   }
}

$b = new B;
$b->foo;
?>

The above example will output:

B


Objects and references> <Type Hinting
Last updated: Fri, 10 Oct 2008
 
add a note add a note User Contributed Notes
Late Static Bindings
lazybones_senior
01-Oct-2008 03:30
WHOA... KEEP IT SIMPLE!

Remember, when you write a class definition, you are creating a new "type" of object. And when you extend those classes, you are creating a heirarchy. To get to the point... all the class defs below work together to provide a solid organization of data.

<?php

abstract class Animal {
  protected
$type, $name;

  public function
__construct($aType, $aName) {
   
$this->type = $aType;
   
$this->name = $aName;
  }

  public function
__toString() {
    return
"Animal [type=$this->type, name=$this->name]";
  }
}

class
Dog extends Animal {
  public function
__construct($aName) {
   
parent::__construct("Dog", $aName);
  }
}

class
Cat extends Animal {
  public function
__construct($aName) {
   
parent::__construct("Cat", $aName);
  }
}

echo
'My dog: ' . (new Dog('Sam')) . '<br>';
echo
'My cat: ' . (new Cat('Fluffy')) . '<br>';
echo
'Your dog: ' . (new Dog('Walter')) . '<br>';
echo
'Yout cat: ' . (new Cat('Ginger'));

?>

My dog:   Animal[type=Dog, name=Sam]
My cat:   Animal[type=Cat, name=Fluffy]
Your dog: Animal[type=Dog, name=Walter]
Yout cat: Animal[type=Cat, name=Ginger]

... and notice the property called $type, is the same as using __CLASS__ in most of the previous posts, but without the complexities of the PHP language.
gern_ at hotmail dot com
18-Sep-2008 10:51
get_called_class for PHP < 5.3

<?php
/**
 * Return called class name
 *
 * @author Michael Grenier
 * @param int $i_level optional
 * @return string
 */
function get_called_class ($i_level = 1)
{
   
$a_debug = debug_backtrace();
   
$a_called = array();
   
$a_called_function = $a_debug[$i_level]['function'];
    for (
$i = 1, $n = sizeof($a_debug); $i < $n; $i++)
    {
        if (
in_array($a_debug[$i]['function'], array('eval')) ||
           
strpos($a_debug[$i]['function'], 'eval()') !== false)
            continue;
        if (
in_array($a_debug[$i]['function'], array('__call', '__callStatic')))
           
$a_called_function = $a_debug[$i]['args'][0];
        if (
$a_debug[$i]['function'] == $a_called_function)
           
$a_called = $a_debug[$i];
    }
    if (isset(
$a_called['object']) && isset($a_called['class']))
        return (string)
$a_called['class'];
   
$i_line = (int)$a_called['line'] - 1;
   
$a_lines = explode("\n", file_get_contents($a_called['file']));
   
preg_match("#([a-zA-Z0-9_]+){$a_called['type']}
                {$a_called['function']}( )*\(#"
, $a_lines[$i_line], $a_match);
    unset(
$a_debug, $a_called, $a_called_function, $i_line, $a_lines);
    if (
sizeof($a_match) > 0)
       
$s_class = (string)trim($a_match[1]);
    else
       
$s_class = (string)$a_called['class'];
    if (
$s_class == 'self')
        return
get_called_class($i_level + 2);
    return
$s_class;
}
?>
kx
14-Sep-2008 12:39
At least as of PHP 5.3.0a2 there's a function get_called_class(), which returns the class on which the static method is called.

<?php

class a {
  static public function
test() {
    print
get_called_class();
  }
}

class
b extends a {
}

a::test(); // "a"
b::test(); // "b"

?>
iamscrumpyjack
07-Sep-2008 12:01
I have been dying to see this issue resolved. I'm very much looking forward to the production release of PHP 5.3...

In my case I have been trying to do the following:

class A {
  function __construct() {
    echo "I was called by " . static::__CLASS__;
  }
}

class B extends A {
  function Foo() {
    echo "I am class " . __CLASS__;
  }
}

$b = new B; // Should echo "I was called by B"
$b->Foo(); // Should echo "I am class B"

At the moment I do the following workaround:

class A {
  function __construct($child) {
    echo "I was called by " . $child;
  }
}

class B extends A {
  function __construct() {
    parent::__construct(__CLASS__);
  }

  function Foo() {
    echo "I am class " . __CLASS__;
  }
}

$b = new B; // Echos "I was called by B"
$b->Foo(); // Echo "I am class B"

As you can see, my current workaround has some overhead and is not as water-tight as the late static binding method.
sebastien at info-conseil dot fr
17-Jul-2008 10:26
Here is a small workaround I made for the static inheritance issue. It's not perfect, but it works.

<?php

// BaseClass class will be extended by any class needing static inheritance workaroud
class BaseClass {
   
// Temporarily stores class name for Entry::getStatic() and Entry::setNextStatic()
   
protected static $nextStatic = false;
   
   
// Returns the real name of the class calling the method, not the one in which it was declared.
   
protected static function getStatic() {
       
// If already stored
       
if (self::$nextStatic) {
           
// Clean and return
           
$class = self::$nextStatic;
           
self::$nextStatic = false;
            return
$class;
        }
       
       
// Init
       
$backTrace = debug_backtrace();
       
$class = false;
       
       
// Walk through
       
for ($i=0; $i<count($backTrace); $i++) {
           
// If a class is defined
           
if (isset($backTrace[$i]['class'])) {
               
// Check if it is not a basic class
               
if (!in_array($backTrace[$i]['class'], array('BaseClass', 'GenericClass'))) {
                    return
$backTrace[$i]['class'];
                } else {
                   
$class = $backTrace[$i]['class'];
                }
            } else {
               
// Returns last known class
               
return $class;
            }
        }
       
       
// Default
       
return $class;
    }
   
   
// If a static method is called within global env, the previous method won't work, so we need to tell BaseClass which
   
public static function setNextStatic($class) {
       
// Save value
       
self::$nextStatic = $class;
    }
}

// Generic class declaring various static methods
class GenericClass extends BaseClass {
    public static
$name = 'Generic';
   
    public function
getName() {
       
$static = get_class_vars(get_class($this));
        return
$static['name'];
    }
   
    public static function
basicClassName() {
        return
self::$name;
    }
   
    public static function
staticClassName() {
       
// Get real name
       
$staticName = self::getStatic();
       
       
// Return final class name
       
$static = get_class_vars($staticName);
        return
$static['name'];
    }
}

// Final class
class SomeClass extends GenericClass {
    public static
$name = 'Some';
   
    public static function
returnClassNameWith($string) {
        return
$string.' : '.self::staticClassName();
    }
}

// Instance call

// Will print 'Some'
$a = new SomeClass();
echo
'Name of $a : '.$a->getName().'<br />';

// Static calls

// Will print 'Generic'
echo 'Basic call to SomeClass::$name : '.SomeClass::basicClassName().'<br />';

// Will print 'Generic'
echo 'Global call to SomeClass::$name : '.SomeClass::staticClassName().'<br />';

// Will print 'Some'
BaseClass::setNextStatic('SomeClass');
echo
'Global call to SomeClass::$name with pre-set : '.SomeClass::staticClassName().'<br />';

// Will print 'Some'
echo 'Internal call to SomeClass::$name : '.SomeClass::returnClassNameWith('This is a ').'<br />';

?>

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::
Anonymous
12-Jul-2008 03:49
Trying to recreate an inheritable static part for an object through a singleton pattern.

<?php
/**
 * "Inheritable static" for PHP < 5.3
 * << Library/Inheritable.php >>
 */

abstract class Inheritable_Static extends Singleton
{
}

abstract class
Inheritable
{
    public static function
getStatic($className)
    {
       
// Use an abstract Singleton
       
return Singleton::getInstance($className . '_Static') ;
    }
   
    public function
goStatic()
    {
        return
self::getStatic(get_class($this)) ;
    }
}

/**
 * Abstract
 * << Library/SayIt/Abstract.php >>
 */

abstract class SayIt_Abstract_Static extends Inheritable_Static
{
    public
$format ;
}

abstract class
SayIt_Abstract extends Inheritable
{
    protected
$_name ;
   
    public function
__construct($name)
    {
       
$this->_name = $name ;
    }
   
    final public function
sayIt()
    {
        echo
sprintf($this->goStatic()->format, $this->_name) . "\n" ;
    }
   
}

/**
 * Concrete
 * << Library/SayIt/Hello.php >>
 */

class SayIt_Hello_Static extends SayIt_Abstract_Static
{
}

class
SayIt_Hello extends SayIt_Abstract
{
    public static function
getStatic() { return parent::getStatic(__CLASS__) ; }
}

/**
 * Test
 */

SayIt_Hello::getStatic()->format = 'Hello %s' ;

$w = new SayIt_Hello('World') ;
$j = new SayIt_Hello('Joe') ;

echo
$w->sayIt() ; // Hello World
echo $j->sayIt() ; // Hello Joe
Andrea Giammarchi
21-Jun-2008 04:06
About static parameters, these work as expected.
<?php
class A {
    protected static
$__CLASS__ = __CLASS__;
    public static function
constructor(){
        return  static::
$__CLASS__;
    }
}

class
B extends A {
    protected static
$__CLASS__ = __CLASS__;
}

echo   
B::constructor(); // B
?>
martinpauly [at] google mail [dot] com
18-Jun-2008 08:54
will this work for variables as well?

it would be great, if the following worked:

<?php
class A {
protected static
$table = "table";
public static function
connect(){
    
//do some stuff here
    
echo static::$table;
     return static::
getInstance(); //function getInstance() now can return classes A or B depending on the context it was called
}
...
}

class
B extends A {
protected static
$table = "subtable";
...
}

$table = B::connect(); //hopefully the output will be: subtable
?>
deadimp at gmail dot com
05-Jun-2008 01:39
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 implementation
class 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 question
       
return $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.

Even more so, is there a way to get the class's name to which the keywords 'self', 'parent', or 'static' refer?
Example:
<?php
class Base {
    static function
stuff() {
        echo
"Self: ".get_class(self);
        echo
"Parent: ".get_class(parent);
        echo
"Derived: ".get_class(static);
    }
}
class
Derived extends Base {
    static function
stuff() {
        static::
stuff();
    }
}
?>

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 say
function& 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 HTML
       
return 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.';'); //using
   
return $temp;
}
?>
tyler AT canfone [dot] COM
05-Jun-2008 01:48
@ php at mikebird

You can pass arguments to your constructor through your getInstance method, assuming you are running php5.

        public static function getInstance($params = null) {
            if (self::$objInstance == null) {
                $strClass = static::getClass();
                self::$objInstance = new $strClass($params);
            }
            return self::$objInstance;
        }

This would pass the params to your constructor. Love for php.
sergei at 2440media dot com
28-May-2008 04:22
Finally we can implement some ActiveRecord methods:

<?php

class Model
{
    public static function
find()
    {
        echo static::
$name;
    }
}

class
Product extends Model
{
    protected static
$name = 'Product';
}

Product::find();

?>

Output: 'Product'
php at mikebird dot co dot uk
23-Apr-2008 11:39
This should make life easier and neater if you have a project with a lot of singleton classes e.g.

<?php

   
class Singleton {
       
        public static
$objInstance;
   
        public static function &
getInstance() {
            if (
self::$objInstance == null) {
               
$strClass = static::getClass();
               
self::$objInstance = new $strClass;
            }
            return
self::$objInstance;
        }
       
        public static function
getClass() {
            return
__CLASS__;
        }
   
    }

    class
Foo extends Singleton {
       
        public
$intBar;
       
        public function
__construct() {
           
$this->intBar = 1;
        }
       
        public static function
getClass() {
            return
__CLASS__;
        }
       
    }
   
   
   
$objFooTwo = Foo::getInstance();
   
$objFooTwo->intBar = 2;
   
   
$objFooOne = Foo::getInstance();
   
    if (
$objFooOne->intBar == $objFooTwo->intBar) {
        echo
'it is a singleton';
    } else {
        echo
'it is not a singleton';
    }

?>

The above will output 'it is a singleton'. The obvious downfall to this method is not being able to give arguments to the constructor.
max at mastershrimp dot com
10-Apr-2008 06:24
If you are using PHP < 5.3.0 you might be interested in the following workaround for late static binding: http://de2.php.net/manual/de/function.get-class.php#77698

Objects and references> <Type Hinting
Last updated: Fri, 10 Oct 2008
 
 
show source | credits | sitemap | contact | advertising | mirror sites