php后期静态绑定

php5.3.0开始,增加了后期绑定功能,用于在继承范围内引用静态调用的类。

要说这个需要先看2个概念

非转发调用(non-forwarding call)

指通过明确指定类名的静态调用(A::test())或非静态调用($a->test())

转发调用(forwarding call)

指通过以下方式的静态调用:self::parent::static::以及forward_static_call(),意思是说没有明确指定类型的静态调用

后期绑定的意思是说,static::不再被解析为定义当前方法所在的类,而是通过实际运行时计算的。可以用于静态非静态方法调用。注意这里可以没有说parent::self::是这样的哦。

后期绑定的工作原理是存储了在上一个"非转发调用"的类名,意思是说当发生非转发调用时,会存储一下这个类名,用于后面转发调用时static动态计算使用。

这里特别指出self::和parent::

使用self::或者__CLASS__对当前类的静态引用,取决于定义当前方法所在的类,使用parent::取决于定义当前方法所在类的父类

看下面代码

<?php
class A {
    public static function foo() {
        static::who();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}

class B extends A {
    public static function test() {
        A::foo();
        parent::foo();
        self::foo();
    }

    public static function who() {
        echo __CLASS__."\n";
    }
}
class C extends B {
    public static function who() {
        echo __CLASS__."\n";
    }
}

C::test();
?>

A
C
C

解析:

1.C类没有test静态方法,所以父类B,这里是非转发调用

2.执行B的test方法,根据后期绑定原理,存储的的类名是C

3.A::foo是非转发调用,执行A的foo方法,里面的static动态计算为A,所以执行A的who方法,结果是A

4.parent::foo,注意A::foo并没有影响这里的存储类名,此时还是C,parent调用所在方法的类的父类,即A类的foo方法,此时A类foo方法中的static计算为C,因为此时存储的类名是C,所以这里相当于是C::who,C有who静态方法,结果为C

5.self::foo,调用方法所在类自己的foo,即B::foo,B没有foo找父类,执行A::foo,此时A::foo方法中的static计算为C,那么情况和4一样,结果为C

在非静态环境下,所调用的类即为该对象实例所属的类。$this->会在同一作用范围内尝试调用私有方法,static所调用的类内的方法。

<?php
class A {
    private function foo() {
        echo "success!\n";
    }
    public function test() {
        $this->foo();
        static::foo();
    }
}

class B extends A {
   /* foo() will be copied to B, hence its scope will still be A and
    * the call be successful */
}

class C extends A {
    private function foo() {
        /* original method is replaced; the scope of the new one is C */
    }
}

$b = new B();
$b->test();
$c = new C();
$c->test();   //fails
?>

success!
success!
success!


Fatal error:  Call to private method C::foo() from context 'A' in /tmp/test.php on line 9

解析:

1.$b->test();执行A的test方法,这里的this是B的实例对象,但是B中没有foo方法,调用A的foo方法,虽然是private,但是此时test方法在A的范围内,所以可以调用,static情况一样

2.$c->test();也是执行A的test方法,这是this是C的实例对象,C有foo方法,但是this尝试调用范围内的私有方法(上面说了),所以调用的是A的foo方法,但是static不同,static这里是C的实例对象调用foo方法,和this不同,static会老老实实去找C中foo方法,但是C的foo方法是private的,由于在A范围内无法调用C的私有方法,所以报错。这里this情况有些特殊,名义上是C的实例,但是寻找范围内的私有方法。即使你把C中的foo方法改为public结果也是一样。

总结

  1. 非转发调用,存储一下类名,用于后期转发调用计算,主要是替换static::
  2. self和parent要根据定义方法所在类去查找
  3. __CLASS__方法的是定义所在类的类名

参考

后期静态绑定

对 PHP 后期静态绑定的理解

posted @ 2020-07-31 23:42  whyly  阅读(263)  评论(0编辑  收藏  举报