PHP中奇怪的魔法__get()行为

时间:2014-02-06 20:45:00

标签: php oop

我正在研究魔法PHP的方法,并提出了非常奇怪的行为。我的代码:

<?php
class Magic
{
    public $a = "A";
    protected $b = ["a" => "A", "b" => "B", "c" => "C"];
    protected $c = [1, 2, 3];

    public function __get($v)
    {
        echo "$v,";
        return $this->b[$v];
    }
}

$m = new Magic();

echo $m->a.",";
echo $m->b.",";
echo $m->c;

echo PHP_EOL;

echo $m->a.",".$m->b.",".$m->c;

输出:

A,b,B,c,C
b,c,A,B,C

我希望得到这样的东西:

A,b,B,,c,,C
A,b,B,,c,,C

为什么输出线不同?

4 个答案:

答案 0 :(得分:3)

我已经编辑了这样的魔术方法,在输出内部添加了。

public function __get($v)
{
    echo " internal $v,";
    return $this->b[$v];
}

我得到了

A, internal b,B, internal c,C
internal b, internal c,A,B,C

因此,当我们调用$m->a时,它会返回a属性,因为它已定义为public

$ b和$ c受到保护,这意味着我们的魔术方法将起作用。

echo $m->a.",";A,,不调用魔法__get方法。因为a是公共财产。

虽然echo $m->b.",";internal b, B,,因为它会调用我们的魔术方法。

我们逐个给出了变量,但在第二次调用中,我们想要同时使用所有三个变量。

在这种情况下,当我们同时将所有三个变量赋予echo命令时;它首先尝试并获取所有这三个,然后在屏幕上打印它们。 (请参阅Joncomment。)

当echo命令收集其信息时,它会为b和c两次调用我们的魔法 __ get 方法,而我们的方法会在屏幕上打印一些内容。 (internal b, internal c,)然后,在我们的魔术方法返回并且echo命令收集了所需的信息之后,它会打印出它所获得的信息,即A,B,C

答案 1 :(得分:2)

执行:echo $m->a.",".$m->b.",".$m->c;时,得到b,c,A,B,C,因为__get的echo语句(小写)是在连接$m->a.",".$m->b.",".$m->c字符串之前执行的,因此在执行之前其中任何一个(A,B,C)都会被回应。

在另一种情况下,您在每次__get调用后回显,因此输出(A,b,B,c,C)具有混合的值。

答案 2 :(得分:2)

这实际上是预期的行为。

当访问bc之类的虚拟财产时,您的代码首先会回显其名称,然后返回其值,然后回显。

echo $m->b.',';

相同
echo "b,"; // echo "$v," from __get(b)
echo "B" . ","; // echo return value from __get(b)

回显PHP_EOL后也是如此。它首先处理所有echo的“参数”,从而回显所有变量名,然后返回值,连接并回显它们。

echo $m->a.",".$m->b.",".$m->c;

相同
echo "b,"; // echo "$v," from __get(b)
echo "c,"; // echo "$v," from __get(c)
echo "A" . ","; // echo value of public property a
echo "B" . ","; // echo return value from __get(b)
echo "C"; // echo return value from __get(c)

答案 3 :(得分:1)

第一行输出应该不足为奇:

echo $m->a.",";  // $m->a is accessible, prints "A,"
echo $m->b.",";  // not accessible, __get called, prints "b,", ret, prints "B,"
echo $m->c;      // not accessible, __get called, prints "c,", ret, prints "C"

您观察到的最终结果应为A,b,B,c,C

乍一看第二行令人惊讶,但如果你想一想,那就不那么多了。它与$m->a.",".$m->b.",".$m->c中的子表达式的评估顺序有关。

从结果中可以明显看出,PHP所做的是首先按照出现的顺序评估所有三个属性读取(这会立即导致副作用b,c,出现)然后才会连接结果。这会生成附加的A,B,C

对于子表达式, PHP 不保证任何特定的评估顺序,无论是在此情况还是在任何其他情况下。这意味着如果你的表情会产生副作用,你必须非常小心,因为这些副作用可能会以反直觉的顺序发现(你已经发现)。

除了探索性练习之外,在PHP(以及任何其他命令式语言)中遵循的一个好的经验法则是不要编写与副作用一起产生结果的代码(这实际上是一个single responsibility principle)的特殊情况。