php 5.2中get_called_class的实现及应用
在PHP5中,我们可以在类中通过self关键字或者\_\_CLASS\_\_来判断或调用当前类。但有一个问题,如果我们是在子类中调用,得到的结果将是父类。因为在继承父类的时候,静态成员就已经被绑定了。
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();
以上代码输出的结果是: A
这和我们的预期不同,我们原来想得到子类的相应结果。
PHP 5.3.0中增加了一个static关键字来引用当前类,即实现了延迟静态绑定,同时PHP 5.3.0也实现get\_called\_class()函数用于查找当前被调用的类,而且允许使用变量作为类名调用静态属性或方法。那么在PHP 5.3.0中要实现上述功能有两种方法(可能不止两种):
class A {
public static $foo = array('A1');
public static function make() {
/*方法一,使用get_called_class()
$test = get_called_class(); //获取当前被调用的类
return $test::$foo; //使用变量作为类名调用静态成员属性
*/
return static::$foo; //方法二,使用static关键字实现静态延迟绑定
}
}
class B extends A {
public static $foo = array('B1');
}
print_r(A::make());
echo "\n";
print_r(B::make());
结果如下:
---------- Debug PHP ----------
Array
(
[0] => A1
)
Array
(
[0] => A1
)
那么在PHP5.3之前我们如何能实现这种功能呢?这几天研究了国外某论坛的某些代码,自己整理了一下: 在PHP5.3之前,即不支持延迟静态绑定,也不支持变量作为类名调用静态属性或方法,更不支持get\_called\_class()函数。我们最好不要去改变PHP内核,所以唯一能做的就是实现get\_clalled\_class()函数,使在上下文中能获取正确的类名,最后用反射类来调用静态属性或方法。看代码:
if(!function_exists('get_called_class')) {
class class_tools
{
private static $i = 0;
private static $fl = null;
public static function get_called_class()
{
$bt = debug_backtrace();
//使用call_user_func或call_user_func_array函数调用类方法,处理如下
if (array_key_exists(3, $bt)
&& array_key_exists('function', $bt[3])
&& in_array($bt[3]['function'], array('call_user_func', 'call_user_func_array'))
) {
//如果参数是数组
if (is_array($bt[3]['args'][0])) {
$toret = $bt[3]['args'][0][0];
return $toret;
}else if(is_string($bt[3]['args'][0])) {//如果参数是字符串
//如果是字符串且字符串中包含::符号,则认为是正确的参数类型,计算并返回类名
if(false !== strpos($bt[3]['args'][0], '::')) {
$toret = explode('::', $bt[3]['args'][0]);
return $toret[0];
}
}
}
//使用正常途径调用类方法,如:A::make()
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();
}
}
class A {
public static $foo = array('A1');
public static function make() {
$test = get_called_class();
$testRef = new ReflectionClass($test);
return $testRef->getStaticPropertyValue('foo');
}
}
class B extends A {
public static $foo = array('B1');
}
// -------------------------------
/*test call_user_func function
$testA = call_user_func(array('A', 'make'));
var_dump($testA);
echo "\n";
*/
// -------------------------------
/*test call_user_func_array*/
$testC = call_user_func_array(array('A', 'make'), array());
var_dump($testC);
// ---------------------------------
/*now you can use call_user_func('B'.'::make');*/
$testB = call_user_func('B'.'::make');
var_dump($testB);
echo "\n";
// ----------------------------------
/*test static function call directly
print_r(A::make());
echo "\n";
print_r(B::make());
*/