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()); */