Свойство многомерного массива только для чтения, PHP - PullRequest
2 голосов
/ 13 июня 2011

Я дурачился с 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]);
    }

}

Ответы [ 2 ]

3 голосов
/ 13 июня 2011

Вам нужно использовать второй класс, реализующий ArrayAccess, чтобы использовать вместо ваших массивов.Затем вы сможете контролировать то, что добавляется в массив, с помощью метода offsetSet():

class ReadOnlyArray implements ArrayAccess {
    private $container = array();
    public function __construct(array $array) {
        $this->container = $array;
    }
    public function offsetSet($offset, $value) {
        throw new Exception('Read-only');
    }
    public function offsetExists($offset) {
        return isset($this->container[$offset]);
    }
    public function offsetUnset($offset) {
        unset($this->container[$offset]);
    }
    public function offsetGet($offset) {
        if (! array_key_exists($offset, $this->container)) {
            throw new Exception('Undefined offset');
        }
        return $this->container[$offset];
    }
}

Затем вы можете инициализировать ReadOnlyArray с вашим исходным массивом:

$readOnlyArray = new ReadOnlyArray(array('foo', 'bar'));
2 голосов
/ 13 июня 2011

Невозможно вернуться по ссылке, что решило бы проблему изменчивости, но не позволило бы изменить некоторые значения, которые можно изменять.

В качестве альтернативы вам нужно обернуть каждый возвращенный массив в ArrayAccess,тоже - и запретить запись туда.

...