Using __set with arrays solved, but why?

2020-03-21 09:39发布

问题:

Having done a bit of research, I eventually came across the answer to a question I was soon to ask here anyways; How do you work with arrays via the __get and __set magic methods in PHP? Whenever I was trying to set a value using something like $object->foo['bar'] = 42; it seemed to silently discard it.

Anyways, the answer is simple; The __get method simply needs to return by reference. And after tossing an ampersand in front of it, sure enough it works.

My question actually, is why? I can't seem to understand why this is working. How does __get returning by reference affect __set working with multidimensional arrays?

Edit: By the way, running PHP 5.3.1

回答1:

In this particular case, __set is not actually getting called. If you break down what it happening, it should make a bit more sense:

$tmp = $object->__get('foo');
$tmp['bar'] = 42

If __get did not return a reference, then instead of assigning 42 to the 'bar' index of the original object, you're be assigning to the 'bar' index of a copy of the original object.



回答2:

In PHP when you return a value from a function you can consider it making a copy of that value (unless it's a class). In the case of __get unless you return the actual thing you want to edit, all the changes are made to a copy which is then discarded.



回答3:

maybe more clear:

//PHP will try to interpret this:
$object->foo['bar'] = 42

//The PHP interpreter will try to evaluate first 
$object->foo

//To do this, it will call 
$object->__get('foo')
// and not __get("foo['bar']"). __get() has no idea about ['bar']

//If we have get defined as &__get(), this will return $_data['foo'] element 
//by reference.
//This array element has some value, like a string: 
$_data['foo'] = 'value';

//Then, after __get returns, the PHP interpreter will add ['bar'] to that
//reference.
$_data['foo']['bar']

//Array element $_data['foo'] becomes an array with one key, 'bar'. 
$_data['foo'] = array('bar' => null)

//That key will be assigned the value 42
$_data['foo']['bar'] = 42

//42 will be stored in $_data array because __get() returned a reference in that
//array. If __get() would return the array element by value, PHP would have to 
//create a temporary variable for that element (like $tmp). Then we would make 
//that variable an array with $tmp['bar'] and assign 42 to that key. As soon 
//as php would continue to the next line of code, that $tmp variable would 
//not be used any more and it will be garbage collected.