__get
is only invoked for non-existent or invisible properties. In other words, when you write
$obj->prop
if prop
is defined and is visible in the current context, it will be returned "as is", without calling __get
.
Example:
class X {
public $pub = 1;
private $pri = 2;
function __get($v) {
echo "[get $v]
";
return 42;
}
function test() {
echo $this->foo, "
"; // __get invoked
echo $this->pri, "
"; // no __get
echo $this->pub, "
"; // no __get
}
}
$x = new X;
$x->test();
echo $x->foo, "
"; // __get invoked
echo $x->pri, "
"; // __get invoked (property not visible)
echo $x->pub, "
"; // no __get
This explains why magic->a
doesn't invoke the getter. Now, since you also have the setter defined, magic->c = CC
actually changes the protected member of the class, therefore, when you echo magic->c
later on, this still invokes the getter (due to c
's invisibility), and the getter returns this->b[c]
, not the actual value of this->c
.
Here's your code, slightly rewritten for clarity:
class Magic
{
public $a = "publicA";
protected $values = array( "a" => "valA" , "b" => "valB" , "c" => "valC" );
protected $c = "oldC";
public function __get( $v )
{
echo "[get $v]
";
return $this->values[$v];
}
public function __set( $var , $val )
{
echo "[set $var=$val]
";
$this->$var = $val;
}
}
$m = new Magic();
echo $m->a . ", " . $m->b . ", " . $m->c . "
";
$m->c = "newC";
echo $m->a . ", " . $m->b . ", " . $m->c . "
";
Results:
[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`
An exercise to the reader:
Why is the output different if you replace dots .
in the echo statements with commas:
$m = new Magic();
echo $m->a , ", " , $m->b , ", " , $m->c , "
";
$m->c = "newC";
echo $m->a . ", " , $m->b , ", " , $m->c , "
";