offsetSet не вызывается в ArrayObject

Пример php кода:

class TestArrayObject extends ArrayObject {
    function offsetSet($index,$val){
        echo $index.':'.$val.PHP_EOL;
    }
}

$s = new TestArrayObject();

//test 1
$s['a'] = 'value';//calls offsetSet
//test 2
$s['b']['c'] = 'value';//does not call offsetSet, why?

var_dump($s);

почему тест 2 не вызывает метод offsetSet?

3 ответа

Решение

ArrayObject это большой кусок магии, и поэтому я бы не рекомендовал использовать его или, что еще хуже, расширить его.

Но ваша настоящая проблема не имеет отношения к ArrayObject само собой. То, что вы сталкиваетесь с тем, что $s['b']['c'] = ...; это так называемая косвенная модификация 'b' смещение. Код, который выполняет PHP, выглядит примерно так:

$offset =& $s['b'];
$offset['c'] = ...;

Как вы можете видеть смещение 'b' никогда не пишется напрямую. Вместо этого он выбирается по ссылке, а ссылка изменяется. Вот почему offsetGet будет называться, а не offsetSet,

Это своего рода "уловка", так как я думаю, что ожидаемое поведение будет таким, как заявленный ОП в этом вопросе.

кроме того, поведение не согласовано при доступе к ArrayObject как к объекту или к массиву.

Пример:

$foo['b']['c'] = 'value'; // no warning
var_dump($foo);

$bar->b->c = 'value'; // triggers warning
var_dump($bar);

У меня были те же проблемы с моим собственным классом, который расширяет ArrayObject. Я не смог исправить проблему, но мне удалось обойти ее, реализовав новый метод, который я использую для создания цепочки свойств.

class Arraylist extends ArrayObject
{
    public function set($key, $value = null)
    {
        if (!is_null($value)) {
            $val = new ArrayList((array) $value);
        } else {
            $val = new ArrayList();
        }       
        $this->offsetSet($key, $val);
        return $this->offsetGet($key);
    }

    public function offsetSet($index,$val){
        echo $index.':'.$val.PHP_EOL;
        parent::offsetSet($index, $val);
    }
}

Используя пример OP:

Код:

$s = new ArrayList();
$s->set('a', 'value');
$s->set('b')->set('c', 'another value');
var_dump($s);

Выходы:

0:value
a:ArrayList
0:value
b:ArrayList
0:another value
c:ArrayList
0:another value

class ArrayList#5 (2) {
  public $a =>
  class ArrayList#7 (1) {
        string(5) "value"
  }
  public $b =>
  class ArrayList#8 (1) {
    public $c =>
    class ArrayList#9 (1) {
            string(13) "another value"
    }
  }
}

Для многомерных массивов смотрите примечания пользователя здесь

Другие вопросы по тегам