Php 神奇的接球手/二传手没有被召唤
当我阅读Zend PHP认证研究指南5.5中的OOP章节时,我发现一个问题,它的回答让我震惊。这个问题是: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 ) {
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";