Johnny Come Lately: PHP Late Static Binding

7 03 2013

http://www.flickr.com/photos/nationallibrarynz_commons/5442760301/sizes/n/in/photostream/

Binding by Zaehnsdorf, 1907

Late static binding (LSB) came late to the PHP5.0 party touting real OOP support, appearing a few so-called minor versions later in PHP5.3.  If you can get past this dreadfully ugly sounding terminology, your code may benefit from a little LSB. Or, not. According to Giorgio Sironi one needs to recognize the “Dangers of Late Static Bindings”.  Other PHP developers have a brighter outlook, such as Lorna Jane who enthusiastically embraces them in “PHP5.3 Feature: Late Static Binding“.

Up to now, I’ve managed to avoid the feature and in fact had nearly all but forgotten about it.  Recently, while reading  an old article from March 2006 about “PHP5 and Polymorphism“, the subject matter resurrected itself.  The author, David Fells (who has since turned his attentions to Drupal)  mentions a problem effecting really old versions of PHP5, such PHP5.0.1.  He relates of PHP being unable to support the late binding of $this and provides a code example as proof.  The incomplete example needs some fleshing out if anyone were to wish to run the code, so I offer the following variation:

<?php
class Person
 {
    function GetPerson( $name) {
        if (class_exists($name) &&
           (($obj = new $name()) instanceof Person))
        {
            return $obj;
        }
    }
    function AddFeedback($messageArray)
    {
        $this->ParseFeedback($messageArray);
        echo 'Writing to database. ',"\n";
    }
 }

 class Shar extends Person
 {
    function ParseFeedback($messageArray)
    {
        echo 'Doing some parsing. ',"\n";
        echo join(' ',$messageArray),"\n";
    }
 }

$name = 'Shar';
$messageArray = array('This','is','the','start',
                      'and','end','of','a','message');
$myPerson = Person::GetPerson($name);
$myPerson->AddFeedback($messageArray);
 

To my amazement, when I ran the code under Windows XP, PHP5.0.1, the code worked perfectly.  Good? Actually, how maddening that code which reputedly fails refuses to behave accordingly!  So, I tried to see if the code would error out on a box running PHP5.1.6 under Linux. Alas, again the code ran successfully. These results are stunning for several reasons.  Apparently, $this lacks any associated late binding issue, which begs the question as to whether an issue of any sort exists with respect to late binding.  Fells’ insistence on PHP having had such an issue, prompted me to ever so slightly tweak the source code.  All I had to do was substitute self for $this,and the  issue becomes clearly evident as running the following code demonstrates:

<?php
class Person
 {
    function GetPerson( $name) {
        if (class_exists($name) &&
           (($obj = new $name()) instanceof Person))
        {
            return $obj;
        }
    }
    function AddFeedback($messageArray)
    {
        self::ParseFeedback($messageArray);
        echo 'Writing to database. ',"\n";
    }
 }

 class Shar extends Person
 {
    function ParseFeedback($messageArray)
    {
        echo 'Doing some parsing. ',"\n";
        echo join(' ',$messageArray),"\n";
    }
 }

$name = 'Shar';
$messageArray = array('This','is','the','start',
                      'and','end','of','a','message');
$myPerson = Person::GetPerson($name);
$myPerson->AddFeedback($messageArray);

Running the code again, I finally struck gold — yes, even errors are valuable!  PHP mistakenly thinks that the Shar object is trying to execute a non-existent method in class Person named ParseFeedback() and is unaware that the object is attempting to execute one of its own methods.  While Fells alludes to a parser error message, instead and more appropriately, a full-fledged fatal error message appears, causing the code to grind to a halt as one would expect for something serious, such as a missing method.

You may note that there is something odd with the above code. I am able to statically obtain an object without instantiating Person despite the fact that the GetPerson() method is bereft of keyword static in its signature!  However, if you run the code with a more modern version of PHP, such as PHP5.4, the parser, imbued with greater knowledge, does complain.  So, how can we bring the code up to today’s way of coding and help PHP connect the dots?  The keyword static can aid by serving to indicate static methods as well as assist the parser to correctly associate a method with an object’s class, as follows:

<?php
class Person
 {
    static function GetPerson( $name) {
        if (class_exists($name) &&
           (($obj = new $name()) instanceof Person))
        {
            return $obj;
        }
    }
    function AddFeedback($messageArray)
    {
        static::ParseFeedback($messageArray);
        echo 'Writing to database. ',"\n";
    }
 }

 class Shar extends Person
 {
    function ParseFeedback($messageArray)
    {
        echo 'Doing some parsing. ',"\n";
        echo join(' ',$messageArray),"\n";
    }
 }

$name = 'Shar';
$messageArray = array('This','is','the','start',
                      'and','end','of','a','message');
$myPerson = Person::GetPerson($name);
$myPerson->AddFeedback($messageArray);

Now, the code runs decently.  By removing the keyword self and substituting static, when the Shar object calls method AddFeedback(), the parser will associate the method as one belonging to the object instead of trying to find it in the object’s parent class Person.

There is also another way the code could be written which also makes use of LSB, namely using the PHP function get_called_class(), as follows:

<?php
class Person
 {
    static function GetPerson( $name) {
        if (class_exists($name) &&
           (($obj = new $name()) instanceof Person))
        {
            return $obj;
        }
    }
    function AddFeedback($messageArray)
    {
        $class_called = get_called_class();
        $class_called::ParseFeedback($messageArray);
        echo 'Writing to database. ',"\n";
    }
 }

 class Shar extends Person
 {
    function ParseFeedback($messageArray)
    {
        echo 'Doing some parsing. ',"\n";
        echo join(' ',$messageArray),"\n";
    }
 }

$name = 'Shar';
$messageArray = array('This','is','the','start',
                      'and','end','of','a','message');
$myPerson = Person::GetPerson($name);
$myPerson->AddFeedback($messageArray);

You may find this last example a bit easier to discern since it clearly seeks to determine the correct class to associate with the method ParseFeedback().

I recommend that you may also wish to read an excellent article by Etienne Kneuss which explains LSB and its nuances, Late Static Bindings Explained

This work is licensed under a Creative Commons License

About these ads

Actions

Information

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s




Follow

Get every new post delivered to your Inbox.

Join 65 other followers

%d bloggers like this: