当我阅读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
,它是如何工作的?
答案 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方法并显示密钥对值。