Archive

Posts Tagged ‘php’

PHP 5.2, late static binding, get_called_class() and $self = new self()

July 29th, 2009 visual77 8 comments

Early on in Septuro’s development, well prior to the name being adopted, I had a tough challenge dealing with the dataTemplates. Chris Webb, one of the Septuro founders, was helping me with this particular issue. In the abstract dataTemplate, we wanted to set up a factory method make() that just pumped out an instance of the class that was called. The issue that we ran into involved late static binding.

class abstract_dataTemplate {
    static function make() {
        return new self();
    }
}
class stringTemplate extends abstract_dataTemplate {

}
stringTemplate::make();

In this example, we called stringTemplate::make(), but since make() is defined within abstract_dataTemplate, the function will always return an instance of abstract_dataTemplate. We found out that in PHP 5.3, late static binding will be better supported and come with a function get_called_class() that will return the name of the called class, rather than the current class. Unfortunately, we run PHP 5.2 on our servers and are unable to use this function. I considered using debug_backtrace(), file() and preg_match() to mimic the functionality, but this won’t work if there is more than 1 instance per line of a late static bind call.

I looked at php.net to see if anyone else found a good solution, but everyone seemed to be stopped at the same point I was. Then I realized I could use static class properties to keep a count of how many times a match has already been found on the current line. This would let me support multiple calls per line without issue – with each call returning the exact class that called it. So I took a function listed in the php.net comments and modified it up to make use of this static class.

if(!function_exists('get_called_class')) {
    class class_tools {
        static $i = 0;
        static $fl = null;

        static function get_called_class() {
            $bt = debug_backtrace();

            if(self::$fl == $bt[2]['file'].$bt[2]['line']) {
                self::$i++;
            } else {
                self::$i = 0;
                self::$fl = $bt[2]['file'].$bt[2]['line'];
            }

            $lines = file($bt[2]['file']);

            preg_match_all('
                /([a-zA-Z0-9\_]+)::'.$bt[2]['function'].'/',
                $lines[$bt[2]['line']-1],
                $matches
            );

            return $matches[1][self::$i];
        }
    }

    function get_called_class() {
        return class_tools::get_called_class();
    }
}

We now have perfect support of get_called_class() in PHP 5.2, and we can use the factory method make() inside the abstract dataTemplate, rather than in all of the derived templates.

Categories: Uncategorized Tags: