What is new in PHP 5.3 – part 2: late static binding

Date November 15, 2007

In the first part of this series I wrote about namespaces, in the second part we will deal with late static binding, which is a very exciting new feature in PHP 5.3 and which promise some really nifty code. :) This topic attracted attention after Zend’s webcast about the Zend Framework. There was a little outcry in the PHP blogosphere, that the ActiveRecord examples presented in this webcast can’t work in PHP even with the version 5.2. Now with late static binding it is possible to implement this style of ActiveRecord almost correctly.

Late static binding basic

First let me introduce what late static binding is about. I am not going to write too much about the basics, because fortunately I get a helping hand :) . So the base problem is the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?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(); // Output: &qout;A&qout;
?>

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, so when we call the test() method on class B it will call the who() method of class A.

PHP 5.3 introduces a new possibility by allowing a keyword that references the class that was initially called at runtime:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
class A {
    public static function who() {
        echo __CLASS__;
    }
    public static function test() {
        static::who(); // Here comes Late Static Bindings   
    }  
}  

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

B::test(); // Output: "B"
?>

This is the basic behavior of the static keyword in this context, you can get more information in the manual. Now back to the ActiveRecord.

Facing the problem

Zend’s proposal was something similar:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

class ActiveRecord {
    public static function findByPk($id)
    {
        // something maqic
    }
}

class Blog extends ActiveRecord {}

Blog::findByPk(1);
?>

The conception is nice, the base implementation of findByPk() finds out from the name of the child class the name of the table on which it should be operate. There is only one (huge) problem with this code, when findByPk() is called it can’t determines the name of the class on which it was called. There was an ugly workaround using debug_backtrace(), but its performance was quite poor, so finally it was not used in ZF.

After the late static binding introduction you may come up with the following code in PHP 5.3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ActiveRecord
{
    public static function getClass()
    {
        return __CLASS__;
    }
   
    public static function findByPk($id)
    {
        $calledClass = static::getClass();
        // The magic...
    }
}

class Blog extends ActiveRecord
{
    public static function getClass()
    {
        return __CLASS__;
    }
}

Blog::findByPk(1);
?>

Running this code the value of $calledClass will be “Blog”. This is great, but it would be very annoying if we have to add the getClass() method to every child class. Fortunately with the late static binding we get a great new function too: get_called_class(), which returns the name of the current calling scope. Using this function we get a nifty code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

class ActiveRecord
{
    public static function findByPk($id)
    {
        $calledClass = get_called_class();
        // The magic remains...
    }
}

class Blog extends ActiveRecord {}

Blog::findByPk(1);
?>

This works very well, but unfortunately we can’t be absolutely happy, because there is a big flaw (I believe it is) in the current implementation of late static binding. Michael Lively pointed to this problem on the internal list and on his blog. How does this problem affect our example? Imagine that we want a simple logging functionality only in the Blog class. Consider the following code (it seems to a logical and suitable solution for this task):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

class ActiveRecord
{
    public static function findByPk($id)
    {
        $calledClass = get_called_class();
        // The magic remains...
    }
}

class Blog extends ActiveRecord
{
    public static function findByPk($id)
    {
        // We want to log something.
       
        // Then the parent should do the magic.
        parent::findByPk($id);
    }
}

Blog::findByPk(1);
?>

If you try to run this program maybe you will surprise, because the value of the $calledClass variable will be “ActiveRecord”. OOPS! What here happens: parent will resolve to ActiveRecord and this breaks late static binding. From the manual: “Late static bindings’ reslution will stop at a fully resolved static call with no fallback”. It is possible that this explanation is not completely exact (maybe somebody from the internal list can explain it more precisely), but the substance is that this is a legitimate usage of late static binding and is not currently supported. I hope it will be fixed/supported in the final version of PHP 5.3.

I believe that this feature holds a lot of potential, for example you should check Joshua Thompson’s very interesting post: Return to Prototype Based Programming in PHP.

23 Responses to “What is new in PHP 5.3 – part 2: late static binding”

  1. developercast.com » Gergely Hodicska’s Blog: What is new in PHP 5.3 - part 2: late static binding said:

    [...] Hodicska has posted part two of his look at the upcoming PHP 5.3 release. In his previous post, he looked at namespacing, but in [...]

  2. Gixx said:

    Awesome!

    I always learn something new.

    The other day I fretted myself because I couldn’t solve the ‘Inherit static methods’ problem when I wanted to create a ‘global’

    public static function GetInstance(){ return new self(); }
    -like stuff.

    So this is what the coders’ nation calls “Late Static Binding”, eh? :) Sweeeeeet.

  3. What is new in PHP 5.3 - part 3: mysqlnd | Gergely Hodicska said:

    [...] the first two parts of this series I wrote about namaspaces and late static binding. In this part I will cover mysqlnd (MySQL native driver for PHP), which is also a really cool [...]

  4. Peter Walther said:

    imho the current 5.3 implementation is right, no need to change. have a look at “other” ;-) oo languages to compare things. solve your problem this way:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    class ActiveRecord
    {
        public static function findByPk($id, $calledClass = null)
        {
              if ($calledClass === null) {
                     $calledClass = get_called_class();
              }
            // The magic remains...
        }
    }

    class Blog extends ActiveRecord
    {
        public static function findByPk($id)
        {
            // We want to log something.
           
            // Then the parent should do the magic.
            parent::findByPk($id, __CLASS__);
        }
    }

    Blog::findByPk(1);

    ————————-
    regards,
    Peter

  5. yahabibi said:

    Maybe you should look at prado framework ? They’re already doing something like that for activerecord.

  6. Felho said:

    @Peter: of course this workaround works, but in my view this is a hack. When you use parent in your code you generally want to extend the parent class. But in this case if you use parent the code in the parent class works in a different way.

  7. Peter Walther said:

    @Felho: I agree but I think we have to accept that PHP will never be a true OO language. I’m looking for ways to keep general/common code in parent classes and more specialized code in subclasses what this hack does. So I don’t have to write duplicate code in subclasses. That’s more important to me than true OO principles. But you are right.

  8. PHP 5.3 : le tour des nouveautés said:

    [...]  What is new in PHP 5.3 – part 2 (0 visite) [...]

  9. Mike Lively said:

    @peter: In Your example you can no longer ‘correctly’ extend Blog without again rewriting the findByPK method.

    So like I have been maintaining, you are just delaying the inevitable.

    @everyone: I have just posted a few separate patches that will allow this to be properly worked around. You can read the details at: http://www.ds-o.com/archives/68-Late-Static-Binding-Changes-to-parent.html

  10. nitrous said:

    Why not make “self” late binding?

  11. Yurtdisi Egitim said:

    it seems like e very good web site but my English is not good. It would be great if it might be availible in other languages too. Thanks.

  12. PHP 5.3: Just keeps getting better and better « PHP::Impact ( [str blog] ) said:

    [...] 1: Namespaces Part 2: Late Static Binding Part 3: mysqlnd Part 4: OpenID support, callStatic, user.ini, XSLT profiling and more. Posted [...]

  13. PHP::Impact ( [str blog] ) » Blog Archive » PHP 5.3: Just keeps getting better and better said:

    [...] 1: Namespaces Part 2: Late Static Binding Part 3: mysqlnd Part 4: OpenID support, callStatic, user.ini, XSLT profiling and more. Posted [...]

  14. Rafael S. Souza said:

    Simple, declare method findByPk as final and be happy! :)

    In many cases this dont solve the problem, but in ActiveRecord implementation this work well, because it dont make sense overwrite basic methods of ActiveRecord class.

  15. What’s new in PHP 5.3 « we’re only gonna die from our arrogance said:

    [...] What is new in PHP 5.3 – part 2: late static binding [...]

  16. Problemas com Late Static Binding | JoEh said:

    [...] vi algumas pessoas reportando um problema com Late Static Binding que, do meu ponto de vista, é facilmente resolvido da seguinte [...]

  17. PHP 5.3 Improvements » EricByers.com said:

    [...] Late static binding [...]

  18. Vysnu » Rails-like ActiveRecord for PHP 5.3 said:

    [...] possible (the magic function __call – equivalent to method_missing in Ruby) and something called late-static binding which is only available in PHP 5.3. __call allows dynamic method invocation and late-static binding [...]

  19. George said:

    This advice is really going to help, thanks.

  20. Dave said:

    I think you may have a security problem – visitors to this page are being redirected to some worrying Russian site. It looks like some dodgy Javascript has been injected at the top of the page.

  21. Singleton and inheritance in PHP : solved in 5.3+ said:

    [...] was only possible with dirty hacks, some of which involving eval(). But in php 5.3 the concept of Late Static Binding was introduced. With that, our problem is gone with only a few code edits.. In the GetInstance [...]

  22. Wim Tibackx said:

    Late Static Binding also proves a great solution for the singleton and inheritance problem prior to php 5.3. Great article! Thanks!

  23. PHP 5.2 Soap Singleton et héritage | Blog de Sébastien NAUER said:

    [...] Page dédiée au Late State Binding [...]

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>