MENU

PHP魔术方法__call()和__callStatci()深入理解

November 13, 2016 • PHP

PHP可以使用静态方式调用非静态方法引入的思考

在看某个ORM类库的时候发现子类使用了subClass::parentMethod()的方式调用,比如self::save(),在记忆里这是静态调用,可是这个save()方法在父类里是一个非静态方法。于是刨根问底的搜索问题答案才有了下面的内容。

魔术方法定义

__call(string $name, array $arguments)
对象中调用一个不可访问方法时,__call()会被触发.

__callStatic(string $name , array $arguments)
使用静态方式调用一个不可访问的方法时,__callStatic()会被触发. 如果调用的方法是非静态方法,那么会报一个Strict Standards提示。

class A
{
    public function __call($name, $args)
    {
        echo "NO\n";
    }

    public static function __callStatic($name, $args)
    {
        echo "YES\n";
    }
}
class B extends A
{
    public function test()
    {
        A::test();
    }
    
    public static function stest()
    {
        A::test();
    }
}

$b = new B; $b->test(); //执行调用
  1. 先看B继承A的情况

    • 当执行到test方法中的A::test()的时候,会去A类中找这个方法,如果找不到恰好又声明了__call()__callStatic()的话__call()就会被调用。那么问题来了,我们明明是用A::test()进行静态调用的,为什么会__call()会被调用,应该是__callStatic()被调用啊混蛋!!!
    • 这个时候看了PHP中的calling scope这篇文章以后,对calling scope有了比较深入的理解。首先明确了在PHP中, 判断静态与否不是靠::(PAAMAYIM_NEKUDOTAYIM)符号, 而是靠calling scope,而$this指针在方法被调用的时候指向的就是这个calling scope。所以当执行A::test()的时候,B对象的calling scope就被传递给了A,那么这个时候就不是静态调用,所以执行了_call()方法。
  2. 再看B没有继承A的情况

    • 当执行到test方法中的A::test()的时候,相当于类外调用。那么这个时候可以分两种情况讨论
    • 如果A类里没有test()方法,就如同上面示例代码,那么这时会在A类里寻找test(),没有则进入到__callStatic()中。如果安装1的情况那么理解应该是进入__call()。所以我的理解是如果A中没有存在所对应的方法,那么此时这个作用域就丢失了,所以就进入了__callStatic()中。
    • 如果A类中有test()方法,那么这个就和鸟哥那篇博客里举的例子一样了,这时候也会把B类的作用域传递到被调用的test()方法中。但是这里PHP5(Strict Standards错误)和PHP7(Deprecated错误)有些不一样,我将在下一篇记录一下。

    参考阅读

  1. http://www.laruence.com/2012/06/14/2628.html
  2. http://www.cnblogs.com/yjf512/archive/2012/09/12/2682556.html
  3. http://www.cnblogs.com/hechunhua/p/3909574.html
  4. https://segmentfault.com/q/1010000000095833
  5. http://www.chanxiaoxi.me/2015/11/18/static-call-non-static-method-in-php/
Last Modified: November 10, 2019