类与对象
在线手册:中文 英文
PHP手册

后期静态绑定

从PHP 5.3.0开始,PHP增加了一个叫做后期静态绑定的功能,用于在继承范围内引用静态调用的类。

该功能从语言内部角度考虑被命名为”后期静态绑定“。”后期绑定“的意思是说,static::不再被解析为定义当前方法所在的类,而是在实际运行时计算的。也可以称之为”静态绑定“,因为它可以用于(但不限于)静态方法的调用。

self:: 的限制

使用self:: 或者 __CLASS__对当前类的静态引用,取决于定义当前方法所在的类:

Example #1 self:: 用法

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

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

B::test();
?>

以上例程会输出:

A

后期静态绑定的用法

后期静态绑定试图通过引入一个关键字表示运行时最初调用的类来绕过限制。简单地说,这个关键字能够让你在上述例子中调用test()时引用的类是B而不是A。最终决定不引入新的关键字,而是使用已经预留的static关键字。

Example #2 static:: 简单用法

<?php
class {
    public static function 
who() {
        echo 
__CLASS__;
    }
    public static function 
test() {
        static::
who(); // 后期静态绑定从这里开始
    
}
}

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

B::test();
?>

以上例程会输出:

B

Note:

static:: 在处理静态方法时与 $this 是不同的。 $this-> 会遵循继承规则,但是 static:: 不会。该差异将稍后在本手册中详细说明。

Example #3 static:: 用于非静态引用

<?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();

?>

以上例程会输出:

TestChild
TestParent

Note:

后期静态绑定的处理方式解决了以往完全没有办法解决的静态调用。另外一方面,如果静态调用使用 parent:: 或者 self:: 将转发调用信息。

Example #4 转发和非转发调用

<?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();
?>

以上例程会输出:

A
C
C

特殊情况

在PHP中有很多方式来触发一个方法的调用,例如回调函数或者魔术方法。因为后期静态绑定取决于运行时的信息,因此在特殊情况下可能会得到意想不到的结果。

Example #5 在魔术方法中使用后期静态绑定

<?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;
?>

以上例程会输出:

B

类与对象
在线手册:中文 英文
PHP手册
PHP手册 - N: 后期静态绑定

用户评论:

fchoquet at NOSPAM dot example dot com (25-Oct-2011 02:36)

A tricky behaviour of LSB:
If you forget a "static" keywork in the method definition you can get very unexpected results.

<?php

class BaseClass {
    public function
callTest() {    // static accidentally omitted
       
static::test();
    }
   
    protected static function
test() {
        echo
'BaseClass::test<br />';
    }
}

class
ExtendedClass extends BaseClass {
    protected static function
test() {
        echo
'ExtendedClass::test<br />';
    }
}

class
UserClass {
    public function
useExtendedClass() {
       
ExtendedClass::callTest(); // Static call of the *acidentally* non static method
   
}

    public function
test() {
        echo
'UserClass::test<br />';
    }
}

BaseClass::callTest();  // BaseClass::test, as expected

ExtendedClass::callTest();  // ExtendedClass::test, as expected

$userClass = new UserClass();
$userClass->useExtendedClass(); // ExtendedClass::test is expected, but displays UserClass::test!!!

?>

brendel at krumedia dot de (21-Mar-2011 02:37)

In my opinion late static binding is an anti pattern but there are some benefits though:

<?php
class MyClass extends ClassReference
{
}
?>

MyClass::getQualifiedName() returns the full class name including namespace information. This prevents you from passing class names as plain strings and still lets you take advantage of code completion and type browsing.

MyClass::cast() ensures the type of an object to be of a certain class. This is a kind of type hinting.

<?php
abstract class ClassReference
{
   
    final public static function
getQualifiedName()
    {
        return
get_called_class();
    }
   
    final public static function
cast($object)
    {
       
$className = get_called_class();
        if (!
is_object($var) || !($object instanceof $className))
        {
            throw new \
UnexpectedValueException("Cast failed. Argument is not of type " . $className . ".");
        }
        return
$object;
    }
   
}
?>

jrfish dot x at gmail dot com (09-Dec-2010 12:05)

also works the same way with static variables and constants

jrfish dot x at gmail dot com (09-Dec-2010 12:01)

consider this:

<?php
class A
{

 
// some stuff....

   
public static function getInstance()
    {
        return new
self();
    }

}

class
B extends A
{
 
//stuff...
}

$obj = B::getInstance();

//versus

class A
{

 
// some stuff....

   
public static function getInstance()
    {
        return new static();
    }

}

class
B extends A
{
 
//stuff...
}

$obj = B::getInstance();
?>

also works the same way with static variables and constants

Anonymous (24-Nov-2010 05:22)

THIS WORKED GREAT FOR ME:

<?php
abstract class ParentClass
{
    static function
parent_method()
    {
       
$child_class_str = self::get_child_class();
        eval(
"\$r = ".$child_class_str."::abstract_static();");
        return
$r;
    }
// CHILD MUST OVERRIDE TO PUT ITSELF INTO TRACE

   
protected abstract static function abstract_static();

    private static function
get_child_class()
    {
       
$backtrace = debug_backtrace();
       
$num = count($backtrace);
        for(
$i = 0; $i < $num; $i++)
        {
            if(
$backtrace[$i]["class"] !== __CLASS__)
                return
$backtrace[$i]["class"];
        }
        return
null;
    }
}

class
ChildClass extends ParentClass
{
    static function
parent_method(){ return parent::parent_method(); }

    protected static function
abstract_static()
    {
        return
__METHOD__."()";
    }
// From ParentClass
}

print
"The call was: ". ChildClass::parent_method();
?>

jakub dot lopuszanski at nasza-klasa dot pl (08-Nov-2010 06:12)

Suprisingly consts are also lazy bound even though you use self instead of static:
<?php
class A{
  const
X=1;
  const
Y=self::X;
}
class
B extends A{
  const
X=1.0;
}
var_dump(B::Y); // float(1.0)
?>

tfn dot yldrm at hotmail dot com (26-Oct-2010 08:30)

example for static binding on 5.2 and real enumaration
<?php
   
/**
    * Static Binding On PHP 5.2
    * @author Tufan Baris YILDIRIM
    * @since 26.10.2010
    */
   
abstract class EnumBase
   
{  
        protected
$num = 0;
        public
$toString;
        public
$toInt;

        public function
__construct($enumKeyOrVal)
        {  

            unset(
$this->toString,$this->toInt);

           
$enums = $this->enums();

            if(
            empty(
$enumKeyOrVal)
            ||
            !(isset(
$enums[$this->num = $enumKeyOrVal])
            ||
            (
$this->num = array_search($enumKeyOrVal,$enums)) !== false)
            )
               
$this->num = 0;

           
/**
            *  5.3 Version
            */
            /*
            *  if(
            empty($enumKeyOrVal)
            ||
            !(isset(static::$enums[$this->num = $enumKeyOrVal])
            ||
            ($this->num = array_search($enumKeyOrVal,static::$enums)) !== false)
            )
            $this->num = 0;
            */
       
}

       
#5.3 e ge?ilirse gerek kalmaz.
       
public function vars()
        {
            return
get_class_vars(get_class($this));
        }

        public function
enums()
        {
           
$vars = $this->vars();
            return
$vars['enums'];
        }

        public function
__get($property)
        {
            if(
method_exists($this,'__'.$property))
                return
$this->{'__'.$property}();
            else
                return
$this->__toString();
        }                                  

        public function
__toInt()
        {
            return
$this->num;
        }

        public function
__toString()
        {

           
$enums = $this->enums();

            if(isset(
$enums[$this->num]))
            {
                return
$enums[$this->num];
            }
            else
            {
                return
$enums[0];
            }

           
/**
            * 5.3 Version
            */

            /*
            if(isset(static::$enums[$this->num]))
            {
            return static::$enums[$this->num];
            }
            else
            {
            return static::$enums[0];
            }
            */
       
}
    }

    class
Positions extends EnumBase
   
{
        public static
$enums = array(
       
0 => 'Bilinmiyor',
       
1 => 'Kale',
       
2 => 'Defans',
       
3 => 'Orta Saha',
       
4 => 'Forvet'
       
); 
    }

    
   
$a = new Positions('Orta Saha');
   
$b = new Positions(4);

   
$c = (string)$a; // Orta Saha
   
$d = (string)$b; // Forvet
?>

fabiolimasouto at gmail dot com (16-Oct-2010 01:10)

<?php
// tricky implementation of singleton
// creates a function handle to work with the child singleton
// so you can get the object doing: ClassName() instead of ClassName::getInstance()
// or get some property rightaway: ClassName('nameOfPublicProperty');
// enjoy!!

Abstract class SingletonBase
{
 protected function
__clone(){}
 public function
getInstance()
 {
  static
$instance = null;
  if(
$instance==null)
  {
  
$instance = new static;
   if(!
function_exists($name=get_called_class()))
   {
   
$strCode = "function {$name}(\$attribute=null){
     static \$obj = null;
     !\$obj AND \$obj =
{$name}::getInstance();
     return \$attribute ? \$obj->\$attribute : \$obj;
    }"
;
   
// creates the getInstance handle function
   
eval($strCode);
   }
  }
  return
$instance;
 }
}

// EXAMPLE

class MySingletonClass extends SingletonBase
{
 var
$varTest = "somevalue";
}

$obj = MySingletonClass::getInstance();
echo
$obj->varTest; // somevalue
echo MySingletonClass('varTest'); // somevalue
$obj = MySingletonClass();
echo
$obj->varTest; // somevalue
?>

steven dot karas+nospam at gmail dot com (27-Sep-2010 10:38)

This function can be used as a workaround for late static binding in PHP >= 5.1.0. There was another similar version of this function elsewhere, but used eval.

<?php

function & static_var($class, $name)
{
    if (
is_object($class))
    {
       
$class = get_class($class);
    }
    elseif ( !
is_string($class))
    {
        throw new
Exception('Must be given an object or a class name', NULL);
    }
   
   
$class = new ReflectionClass($class);
    return
$class->getStaticPropertyValue($name);
}

?>

adam dot prall at thinkingman dot com (11-Jun-2010 05:51)

Just a quick reminder to always check your syntax. While I love LSB, I thought it wasn't working:

static::$sKey = not set

…until I realized that I’d completely forgotten to make it a variable variable:

$sKey = 'testStaticClassVarNameThatExistsInThisClassesScope';

static::$$sKey = is set

…of course this applies anywhere in PHP, but because of the (current) newness late static bindings, I’ve seen lots of code with this particular snafu in it from others.

James (29-Apr-2010 11:59)

If you're still stuck on a pre-5.3 codebase then you can fake a singleton superclass like this.  It ain't pretty, though.  To do it you pass the type info down the stack manually and then abuse call_user_func to pass the value back to the originating class so that you can access it's static properties.

<?php
abstract class Singleton {
    protected function
__construct() {}
    protected function
__clone() {}

    public function
instance($class) {
       
$member = 'instance';
       
$instance = self::getStaticMember($class, $member);
        if (!
$instance instanceof $class) {
           
$instance = new $class();
           
self::setStaticMember($class, $member, $instance);
        }
        return
$instance;
    }
   
    protected function
setStaticMember($class, $member, $value) { call_user_func(array($class, __FUNCTION__), $member, $value); }
    protected function
getStaticMember($class, $member) { return call_user_func(array($class, __FUNCTION__), $member); }
}

class
MySingleton extends Singleton {
    protected static
$instance = null;

    public function
instance() { return parent::instance(__CLASS__); }

    protected function
setStaticMember($member, $value) { self::$$member = $value; }
    protected function
getStaticMember($member) { return self::$$member; }
}

?>

joost dot t dot hart at planet dot nl (21-Aug-2009 02:34)

PHP5.3 unavailable, yet in the need for 'static', I did the following.

Any objections? Personally I hate using the the eval() statement...

<?php

class mother
{
    function
setStatic( $prop, $val ) {
       
// After this, self:: refers to mother, yet next $class refers to...
        //
       
$class = get_class( $this );
        eval(
"$class::\$$prop = \$$val;" );
    }
}

class
child extends mother
{
    protected static
$sProp;

    function
writer( $value ) {
       
parent::setStatic( 'sProp', $value );
    }
    function
reader()
    {
        return
self::$sProp;
    }
}

$c = new child();
$c->writer( 3 );
echo
$c->reader(); // 3

?>

tom (03-Jul-2009 02:28)

Something you may find useful for passive code-testing:

<?php
class BaseClass {
  function
__get($id) {
    throw new
Exception("Trying to access undefined property '$id'.");
  }
  function
__set($id) {
    throw new
Exception("Trying to access undefined property '$id'.");
  }
}

class
MyClass extends BaseClass {
// my implementation
}
?>

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.

kenneth at kennethjorgensen dot com (30-Mar-2009 10:19)

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);
    }
   
    public static function
getInstance() {
       
$class = get_called_class();
        if (
array_key_exists($class, self::$instances) === false)
           
self::$instances[$class] = new $class();
        return
self::$instances[$class];
    }
}

class
A extends Singleton {
}

class
B extends Singleton {
}

$a1 = A::getInstance();
$a2 = A::getInstance();
$b1 = B::getInstance();
$b2 = B::getInstance();

if (
get_class($a1) == "A" &&
   
get_class($a2) == "A" &&
   
get_class($b1) == "B" &&
   
get_class($b2) == "B" &&
   
$a1 === $a2 &&
   
$b1 === $b2)
    echo
"All good\n";
else
    echo
"FAIL!\n";

?>

You probably noticed the use of self:: rather than static::, this is because we want the static variable to be private, and using static:: will not allow us to do that.

lazybones_senior (01-Oct-2008 08: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 03: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 05: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 05: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 03: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 08: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 09: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 01: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 06: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 06: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 09: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 04: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 11: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