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

http://www.septuro.com/2009/07/php-5-2-late-static-binding-get_called_cl...

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.
view plaincopy to clipboardprint?

1. class abstract_dataTemplate {
2. static function make() {
3. return new self();
4. }
5. }
6. class stringTemplate extends abstract_dataTemplate {
7.
8. }
9. stringTemplate::make();

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.
view plaincopy to clipboardprint?

1. if(!function_exists('get_called_class')) {
2. class class_tools {
3. static $i = 0;
4. static $fl = null;
5.
6. static function get_called_class() {
7. $bt = debug_backtrace();
8.
9. if(self::$fl == $bt[2]['file'].$bt[2]['line']) {
10. self::$i++;
11. } else {
12. self::$i = 0;
13. self::$fl = $bt[2]['file'].$bt[2]['line'];
14. }
15.
16. $lines = file($bt[2]['file']);
17.
18. preg_match_all('
19. /([a-zA-Z0-9\_]+)::'.$bt[2]['function'].'/',
20. $lines[$bt[2]['line']-1],
21. $matches
22. );
23.
24. return $matches[1][self::$i];
25. }
26. }
27.
28. function get_called_class() {
29. return class_tools::get_called_class();
30. }
31. }

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.