魔术吸气剂/定型器未被调用

时间:2014-11-22 12:47:11

标签: php oop echo

当我阅读Zend PHP认证学习指南5.5中的OOP章节时,我发现了一个让我对其答案感到震惊的问题。这个问题是:

class Magic
{
    public $a = "A";
    protected $b = array( "a" => "A" , "b" => "B" , "c" => "C" );
    protected $c = array( 1 , 2 , 3 );

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

    public function __set( $var , $val )
    {
        echo "$var: $val,";
        $this->$var = $val;
    }
}

$m = new Magic();
echo $m->a . ", " . $m->b . ", " . $m->c . ", ";
$m->c = "CC";
echo $m->a . ", " . $m->b . ", " . $m->c . ", ";

此代码的输出为:

b, c, A, B, C, c: CC, b, c, A, B, C

为什么此代码不能打印a,它是如何工作的?

4 个答案:

答案 0 :(得分:9)

仅对不存在或不可见的属性调用

__get。换句话说,当你写

$obj->prop

如果prop已定义并且在当前上下文中可见,则会返回"按原样#34;而不调用__get

示例:

class X {

    public   $pub = 1;
    private  $pri = 2;

    function __get($v) {
        echo "[get $v]\n";
        return 42;
    }

    function test() {
        echo $this->foo, "\n";  // __get invoked
        echo $this->pri, "\n";  // no __get
        echo $this->pub, "\n";  // no __get

    }
}

$x = new X;
$x->test();

echo $x->foo, "\n"; // __get invoked
echo $x->pri, "\n"; // __get invoked (property not visible)
echo $x->pub, "\n"; // no __get

这解释了为什么magic->a没有调用getter。现在,由于您还定义了setter,magic->c = CC实际上更改了类的 protected 成员,因此,当您稍后回显magic->c时,这仍然会调用getter(由于c的隐身),getter返回this->b[c],而不是this->c的实际值。

这是您的代码,为了清晰起见,稍作重写:

class Magic
{
    public $a = "publicA";
    protected $values = array( "a" => "valA" , "b" => "valB" , "c" => "valC" );
    protected $c = "oldC";

    public function __get( $v )
    {
        echo "[get $v]\n";
        return $this->values[$v];
    }

    public function __set( $var , $val )
    {
        echo "[set $var=$val]\n";
        $this->$var = $val;
    }
}

$m = new Magic();
echo $m->a . ", " . $m->b . ", " . $m->c . "\n";
$m->c = "newC";
echo $m->a . ", " . $m->b . ", " . $m->c . "\n";

结果:

[get b]      
[get c]
publicA, valB, valC  # no getter for `a` 
[set c=newC]
[get b]
[get c]              # getter still invoked for `c`
publicA, valB, valC  # no getter for `a`

对读者的练习:

如果用逗号代替echo语句中的点.,为什么输出会有所不同:

$m = new Magic();
echo $m->a , ", " , $m->b , ", " , $m->c , "\n";
$m->c = "newC";
echo $m->a . ", " , $m->b , ", " , $m->c , "\n";

答案 1 :(得分:6)

实际上,这是打印的内容(demo):

bcA, B, C, c: CC,bcA, B, C,

关键是,$m->a . ", " . $m->b . ", " . $m->c . ", "表达式的每个部分都会在echo打印结果之前进行评估。在这种情况下,这种评估会产生副作用。

正如@georg演示的那样,如果代码被编写为echo $m->a, ', ', $m->b, ', ', $m->c, ', ',那将会有所不同:在这种情况下,每个操作数在评估后都会立即发送到输出!

$m->a部分很容易评估,因为a是公共财产;它的值是字符串'A'

然而,

$m->b部分是一个棘手的问题:b是受保护的属性,因此要调用魔术方法__get()。此方法打印b(访问的属性的名称)并返回B(值$this->b['b'])。这就是为什么你看到你的行以b开头的原因 - 在评估整个表达式之前打印它!

$m->c也是如此,因为c也是受保护的属性:getter itsels打印c,但返回C$this->b['c']的值),将作为整个表达的一部分打印出来。

在处理$m->c = "CC"行之后,调用另一个 - setter - 魔术方法(因为c受到保护,请记住)。此方法打印c: CC,,因为$var等于要设置的属性的名称,$val是其新值(传递到__set)。< / p>

但即使魔术设置器最终改变了c属性的值,代码中的下一行也将输出与之前相同的字符串。这是因为魔术吸气者永远不会访问$this->c - 当查询$this->b['c']时,它会返回c的值。


最重要的是:认证指南和各种类似风味的测试都喜欢魔术方法,在真实的代码中,除非你真的知道自己会得到什么,否则最好避免它们。通常这些东西都隐藏在框架的深处。核心,作为高级抽象的引擎;你需要提供另一个级别的高级抽象,非常罕见。

答案 2 :(得分:2)

你得到的输出与我得到的输出不匹配:

bcA, B, C, c: CC,bcA, B, C,

尽管如此,本课程通过你可能遇到的overloading __get()__set()来展示magic methods。我认为你混淆的原因可能就是这些信件的出现顺序。

由于__get()魔术方法包含实际属性名称的echo,因此可能得到abc如果没有这个小细节:

/**  Overloading is not used on declared properties.  */
public $a = "A";

因此,由于表达式访问属性a的魔术方法,它将打印属性 name 相反,它打印。记下echo函数的执行顺序。 getter magic方法中的echo在类之外的echo之前执行。

注意:

  

PHP对&#34;重载&#34;的解释与大多数对象不同   面向语言。传统上重载提供了能力   有多个方法,名称相同但数量不同   论证的类型。

答案 3 :(得分:1)

这是因为你的类中有__get方法。它总是触发get方法并调用$b数组值。

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

每次调用$m->a . ", " . $m->b . ", " . $m->c . ", ";时,它都会触发__get方法并显示密钥对值。

类似地,

$m->c = "CC";

触发__set方法并显示设定值。

public function __set( $var , $val )
{
    echo "$var: $val,";
    $this->$var = $val;
}

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

现在再次触发__get方法并显示密钥对值。