Php 神奇的接球手/二传手没有被召唤

Php 神奇的接球手/二传手没有被召唤,php,oop,echo,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 ) {

当我阅读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
,以及它是如何工作的?

\uu get
仅对不存在或不可见的属性调用。换句话说,当你写作的时候

$obj->prop
如果定义了
prop
,并且在当前上下文中可见,则将返回“原样”,而不调用
\u 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
实际上更改了类的受保护成员,因此,当您稍后回显
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";

实际上,下面是打印的内容():

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

正如@georg所演示的,如果代码被写成
echo$m->a',',$m->b',',$m->c',',
,那么情况就大不相同了:在这种情况下,每个操作数都会在求值后立即被发送到输出

$m->a
零件易于评估,因为a
是公共财产;它的值是string
'A'

然而,
$m->b
部分是一个棘手的部分:
b
是一个受保护的属性,因此需要调用magic method
\u get()
。此方法打印
b
(访问的属性名称)并返回
b
(值
$This->b['b']
)。这就是为什么您会看到以
b
开头的行-它是在计算整个表达式之前打印的

$m->c
也会发生同样的情况,因为
c
也是一个受保护的属性:getter itsels打印
c
,但返回
c
(值
$this->b['c']
),它将作为整个表达式的一部分打印

在处理完
$m->c=“CC”
行之后,调用另一个-setter-magic方法(记住,当
c
受到保护时)。此方法打印的是
c:CC,
,因为
$var
等于要设置的属性的名称,
$val
是它的新值(传递到
\u set

但是,即使magic setter最终更改了
c
属性的值,代码中的下一行仍将输出与前面相同的字符串。这是因为魔法getter从不访问
$this->c
——当查询
c
时,它返回
$this->b['c']
的值



底线是:认证指南和各种类似风格的测试都喜欢神奇的方法,在真正的代码中最好避免它们,除非你真的知道你将得到什么。)通常这些东西隐藏在框架的核心深处,充当高级抽象的引擎;很少需要在这个基础上提供另一个层次的高级抽象。

这是因为类中的_uget方法。它总是触发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 . ", ";

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

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

bcA, B, C, c: CC,bcA, B, C,
然而,这个类是通过您可能遇到的
\uu get()
\uu set()
来演示的。我认为你感到困惑的原因可能是信件的发出顺序

由于
\uuu get()
魔术方法包含实际属性名称的
回音
,如果不是因为这个小细节,您可能会得到
abc

/**  Overloading is not used on declared properties.  */
public $a = "A";
因此,由于表达式不访问属性
a
的神奇方法,因此它将打印属性名称,而不是打印值。注意执行
echo
功能的顺序。getter magic方法中的
echo
在类外的echo之前执行

注:

PHP对“重载”的解释与大多数对象不同 面向对象的语言。重载传统上提供了 具有多个名称相同但数量和数量不同的方法 参数的类型


出于好奇,他们真的写了
echo“$v”在教科书中?你有
public$a
属性,getter不在这里工作)另一个问题是魔法:
谁打印额外的逗号和空格?
@lvaro G.Vicario是的,我按照书中的方式编写代码!为了完整起见,这给出了书中OP/给出的确切结果。你说
它将按原样返回
,但为什么不打印
$a的值呢(它是
a
),就在所有神奇的方法都用自己的打印完成之后。@georg这是一个精彩的练习。)在阅读这个问题时,我实际上误读了逗号的圆点——我想知道为什么
echo
必须等待。)@raina77ow:是的,问题中的代码要求一个人同时理解get/set内容和评估顺序
/**  Overloading is not used on declared properties.  */
public $a = "A";