Я дурачился с ArrayAccess
и магией PHP (__get
, __set
) уже некоторое время, и я застрял.
Я пытаюсь реализовать класс, в котором некоторые свойства, которые являются массивами, доступны только для чтения. Первоначально они будут установлены конструктором, но после этого не должны изменяться.
Используя __get
magic по ссылке, я могу получить доступ к элементам массива произвольно глубоко в свойствах, и я подумал, что могу генерировать исключения, когда эти свойства нацелены через __set
.
Однако проблема заключается в том, что когда я обращаюсь к значению элемента массива, PHP вызывает __get
, чтобы вернуть эту часть массива по ссылке, и я не знаю, является ли он доступным для чтения или записи. действие.
( Хуже всего то, что я знал, что это происходит, но дурачился с ArrayAccess
в качестве возможного обходного решения, учитывая, что свойства были экземплярами реализованного объекта )
Простой пример:
class Test{
public function &__get($key){
echo "[READ:{$key}]\n";
}
public function __set($key, $value){
echo "[WRITE:{$key}={$value}]\n";
}
}
$test = new Test;
$test->foo;
$test->foo = 'bar';
$test->foo['bar'];
$test->foo['bar'] = 'zip';
И вывод:
[READ:foo]
[WRITE:foo=bar]
[READ:foo]
[READ:foo] // here's the problem
Реально, мне все равно нужно только значение foo
( согласно моему примеру ), но мне нужно знать, что это действие записи, а не чтения.
Я уже наполовину признал, что этого нельзя достичь, но я все еще надеюсь. Кто-нибудь знает, как можно добиться того, чего я хочу достичь?
Я рассматривал некоторые возможные обходные пути с ArrayAccess
, но, насколько я могу судить, я вернусь к этому месту, учитывая, что я собираюсь использовать нотацию свойств, которая вызывает __get
.
Обновление: Еще один веселый день с ArrayAccess
.
( Это другая проблема, но я предполагаю, что она работает. Публикация только для ударов. )
class Mf_Params implements ArrayAccess{
private $_key = null;
private $_parent = null;
private $_data = array();
private $_temp = array();
public function __construct(Array $data = array(), $key = null, self $parent = null){
$this->_parent = $parent;
$this->_key = $key;
foreach($data as $key => $value){
$this->_data[$key] = is_array($value)
? new self($value, $key, $this)
: $value;
}
}
public function toArray(){
$array = array();
foreach($this->_data as $key => $value){
$array[$key] = $value instanceof self
? $value->toArray()
: $value;
}
return $array;
}
public function offsetGet($offset){
if(isset($this->_data[$offset])){
return $this->_data[$offset];
}
// if offset not exist return temp instance
return $this->_temp[$offset] = new self(array(), $offset, $this);
}
public function offsetSet($offset, $value){
$child = $this;
// copy temp instances to data after array reference chain
while(!is_null($parent = $child->_parent) && $parent->_temp[$child->_key] === $child){
$parent->_data[$child->_key] = $parent->_temp[$child->_key];
$child = $parent;
}
// drop temp
foreach($child->_temp as &$temp){
unset($temp);
}
if(is_null($offset)){
$this->_data[] = is_array($value)
? new self($value, null, $this)
: $value;
}else{
$this->_data[$offset] = is_array($value)
? new self($value, $offset, $this)
: $value;
}
}
public function offsetExists($offset){
return isset($this->_data[$offset]);
}
public function offsetUnset($offset){
unset($this->_data[$offset]);
}
}