关于PHP5 Static的实现 引发的一些问题,使用时要注意

这几天在将我的doggy框架移植到PHP5的时候发现了一些小问题,主要是PHP5的static的实现上和其他的OO语言有很大的不同。
先看一部分代码


class A{
    protected static $v='A';
    public static function getV(){
         return self::$v;
    }
    public static function setV($v){
         self::$v=$v;
    }
}
class B extends A{
    protected static $v='B';
}
class C extends B{
    protected static $v='C';
}
echo 'A::getV '.A::getV();
echo 'B::getV '.B::getV();
echo 'C::getV '.C::getV(); 


按通常的理解,应该是:


A::getV A
B::getV B
C::getV C

但输出的结果是:

A::getV A
B::getV A
C::getV A

再做个实验:

class A {
    protected static $v='A';
    public static function getV(){
         return self::$v;
    }
    public static function setV($v){
         self::$v=$v;
    }
}
class B extends A{
    protected static $v='B';
    public static function getV(){
         return self::$v;
    }
}
class C extends B{
    protected static $v='C';
}
echo 'A::getV '.A::getV();
echo 'B::getV '.B::getV();
echo 'C::getV '.C::getV(); 

猜猜结果是什么?

A::getV A
B::getV B
C::getV B

最后一个例子:



class A{
    protected static $v='A';
    public static function getV(){
         return self::$v;
    }
    public static function setV($v){
         self::$v=$v;
    }
}
class B extends A{
    protected static $v='B';
    public static function getV(){
         return self::$v;
    }
    public static function setV($v){
         self::$v=$v;
    }
}
class C extends B{
    protected static $v='C';
}
A::setV('C');
B::setV('D');
C::setV('E');

echo 'A::getV '.A::getV();
echo 'B::getV '.B::getV();
echo 'C::getV '.C::getV();

输出结果:


A::getV C
B::getV E
C::getV E


ok

看到这里应该清楚了,PHP5的static的实现比较“特别”,从手册里面也能看到说明。
static方法是编译时确定的,而不是按照调用时的继承关系来确定。
简单的说,当调用static方法时,其起始的范围是实现这个static方法(包括重载)的类范围,
而不是按照调用的继承关系的类。比如调B::getV(),如果B类中定义或者重载了getV,那么
此时确定的当前类就是B,否则向上追溯到其父类假设是A,如果A扔没有,继续向上查找,
一旦找到,那么当前类就是这个定义getV的类。此时self指向的就是这个确定的类,而不是
起始调用getMethod的那个class,因此,如果此时这个类假设为A没有定义static $v,则会引发一个错误。

PHP5的这种方式导致了很多困惑,尤其希望实现static重载的时候,你无法通过在父类定义通用
的方法,而试图通过子类override一些static field来实现static级别的继承。

我在用PHP5实现Active Record模式的时候遇到了这些问题,最后只能通过一些hack的手法来实现。

不知道PHP6是否能够改变这种方式,希望如此。

New:

PHP6中通过引入static::来实现late static binding,从而解决了这个问题。

[http://www.php.net/~derick/meeting-notes.html#late-static-binding-using-this-without-or-perhaps-with-a-different-name](http://www.php.net/~derick/meeting-notes.html#late-static-binding-using-this-without-or-perhaps-with-a-different-name)

BTW,Javascript/AS都是prototype-based,和php class-based是不一样的,
[http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Class-Based_vs._Prototype-Based_Languages](http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Guide:Class-Based_vs._Prototype-Based_Languages)

Comments

Leave a Reply