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

search for in the

Objects and references> <Подсказване на тип
Last updated: Fri, 02 Jan 2009

view this page in

Късно статично свързване

От 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

Употреба на късно статично свързване

С късното статично свързване се прави опит да се премахне това ограничение чрез въвеждането на нова ключова дума, която да сочи към класа, който първоначално е бил извикан по време на изпълнение. На практика това е ключовата дума, която би позволила да се извика B от test() в предишния пример. Веше решено вместо да се въвежда нова ключова дума, да се използва 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

Забележка: 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

Забележка: Решението кой метод да се изпълни при късното статично свързване ще бъде взето при напълно определено статично извикване, без връщане. От друга страна, статичните извиквани посредством ключовите думи като 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


add a note add a note User Contributed Notes
Късно статично свързване
lazybones_senior
01-Oct-2008 09: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 04: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 06: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 06: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 04: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 09: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 10: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 02: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 07: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 07: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 10: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 05: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
11-Apr-2008 12: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

 
show source | credits | stats | sitemap | contact | advertising | mirror sites