在PHP中的__call、__callStatic和调用范围

3

我最近了解到 PHP 中的作用域调用和作用域解析运算符 (::)。它们有两种变体:实例调用和静态调用。请看下面的示例:

<?php

class A {
    public function __call($method, $parameters) {
        echo "I'm the __call() magic method".PHP_EOL;
    }

    public static function __callStatic($method, $parameters) {
        echo "I'm the __callStatic() magic method".PHP_EOL;
    }
}

class B extends A {
    public function bar() {
        A::foo();
    }
}

class C {
    public function bar() {
        A::foo();
    }
}

A::foo();
(new A)->foo();

B::bar();
(new B)->bar();

C::bar();
(new C)->bar();

执行结果(PHP 5.4.9-4ubuntu2.2)如下:
I'm the __callStatic() magic method
I'm the __call() magic method
I'm the __callStatic() magic method
I'm the __call() magic method
I'm the __callStatic() magic method
I'm the __callStatic() magic method

我不明白为什么对于 (new C)->bar();,会执行A__callStatic()方法?实例调用应该在bar()方法的上下文中进行,不是吗?这是PHP的特性吗?
补充1:
此外,如果我不使用魔术方法并进行显式调用,一切都按预期工作:
<?php

class A {
    public function foo() {
        echo "I'm the foo() method of A class".PHP_EOL;
        echo 'Current class of $this is '.get_class($this).PHP_EOL;
        echo 'Called class is '.get_called_class().PHP_EOL;
    }
}

class B {
    public function bar() {
        A::foo();
    }
}

(new B)->bar();

这个的结果是:

I'm the foo() method of A class
Current class of $this is B
Called class is B

4
我更关心的是为什么 C::bar(); 没有抛出错误。 - Jason McCreary
为什么?我认为在这种情况下它是正确的。 - vasayxtx
这很可能是调用了非静态方法。并且很可能会抛出“E_STRICT”异常。 - Jason McCreary
从我的php.ini文件中可以看到:error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT - vasayxtx
1
@JasonMcCreary 是的,您是正确的。我在开发中必须在 error_reporting 中使用 E_STRICT 常量,我忘记了 :) - vasayxtx
B::bar() 应该会返回一些错误。 - geckob
2个回答

3
Cbar()方法中,您使用了A::foo();:
public function bar() {
    A::foo();
}

由于该方法既没有创建 A 的实例,也没有让 C 扩展 A,因此 :: 运算符被视为静态运算符,试图调用静态方法 A::foo()。由于 foo()A 上未定义,它会回退到使用 __callStatic() 方法。
如果您想在不扩展 A 的情况下调用非静态方法,则需要创建 A 的实例:
class C {
    public function bar() {
        $aInstance = new A();
        $aInstance->foo();
    }
}

:: 不总是表示静态调用,它取决于调用范围。对于 (new B)->bar();,将执行 B 类的 bar() 方法,然后执行 A::foo(),在这种情况下会进行 __callStatic(),因为它是实例调用范围。 - vasayxtx
@vasayxtx 抱歉,我想我的措辞不太好。我的意思是在 C.bar() 的上下文中,它被用作一个“静态”运算符。让我重新措辞一下,希望能更清楚地表达。 - newfurniturey

0
这是因为在这种情况下,我们没有 A 类的实例。 请注意。
 class B extends A

因此,new B 为我们提供了访问非静态版本的 A->foo 的途径。

C 不扩展 A,因此只有 A 的静态方法可用。


好的,我明白了。对我来说显而易见不够明显。 - vasayxtx
别担心。这些东西可能会变得棘手,并且需要一些注意力,以免忽略一些小但重要的细节。 - Mchl
类C没有继承A,因此只有A的静态方法可用。 我可以在手册中阅读相关内容吗? - vasayxtx
你能看一下我的代码添加1并解释一下为什么它按预期工作吗?你的说法只适用于魔术方法__call()吗? - vasayxtx

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接