Проблема с расширением функции ArrayObject::offsetGet() для возврата null, если элемент не в массиве

Первоначально я думал, что это будет кусок пирога.. не для меня..

Я пытаюсь расширить функцию offsetGet(), чтобы она возвращала значение null, если элемент не находится в ArrayObject. До сих пор я не могу заставить его работать без ошибок.

php -v: 5.3.29

Что я делаю неправильно? Ниже мой код и ошибка:

Мой расширенный класс ArrayObject:

class IssetArray extends \ArrayObject {

    public function &offsetGet($offset) {
        $var = $this->offsetExists($offset) ? parent::offsetGet($offset) : null;
        return $var;
    }
}

Вот как я это называю:

$array = new \IssetArray();

$array['item'] = 123;
var_dump($array['item']);

var_dump($array['something']['noItem']);

$array['something']['foo'] = 'bar';
var_dump($array['something']['foo']);

$normalArrayObject = new \ArrayObject();
$normalArrayObject['something']['foo'] = 'bar';
var_dump($normalArrayObject['something']['foo']);
var_dump($normalArrayObject['something']['noItem']);

Результаты:

int(123)
NULL
Notice: Indirect modification of overloaded element of \IssetArray has no effect in -- on line --
NULL
string(3) "bar"
Notice: Undefined index: noItem in -- on line --
NULL

Что я делаю неправильно?? Если я вызову обычный ArrayObject, я не получу ошибку косвенной модификации. Я так смущен в этот момент.

Любая помощь будет замечательной. Я гуглил и гуглил без везения.

Обновить -----------

При попытке сделать то же самое с ArrayAccess я сталкиваюсь с той же проблемой. Как я могу обойти это с помощью ArrayAccess?

Моя реализация:

class IssetArray implements \ArrayAccess {

    private $container = array();

    public function __construct() {}

    public function offsetSet($offset, $value) {
        if (is_null($offset)) {
            $this->container[] = $value;
        } else {
            $this->container[$offset] = $value;
        }
    }

    public function offsetExists($offset) {
        return isset($this->container[$offset]);
    }

    public function offsetUnset($offset) {
        unset($this->container[$offset]);
    }

    public function &offsetGet($offset) {
        $var = isset($this->container[$offset]) ? $this->container[$offset] : null;

        return $var;
    }
}

Это приводит к той же проблеме, которую я вижу с ArrayObject. Примечание: Косвенное изменение перегруженного элемента \IssetArray не имеет эффекта -- в строке --


person RonSper    schedule 28.05.2015    source источник


Ответы (2)


Краткий ответ: это неприятный побочный эффект перегрузки метода offsetGet() из ArrayObject.

$array['something']['foo'] = 'bar';

В рамках присваивания вызывается ArrayObject::offsetGet('something'), и ожидается, что возвращаемое значение будет возвращать ссылку; проблема в том, что, несмотря на определение &offsetGet(), на самом деле он не возвращает ссылку.

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

Кстати, этого не произойдет, если вместо этого вы реализуете ArrayAccess; конечно, вы не получите все методы, которые идут с ArrayObject :(

Обновлять

Из этого отчета следует, что HHVM демонстрирует правильное поведение, а PHP 7 не будет генерировать никаких уведомлений (но имеет неправильные поведение во время выполнения заданий).

person Ja͢ck    schedule 28.05.2015
comment
Я попытался реализовать с помощью ArrayAccess и столкнулся с теми же проблемами. Должно быть, я что-то упустил в своей реализации. Я снова посмотрю на ArrayAccess и отправлю свой код на свой вопрос. У вас сложилось впечатление, что я могу делать с ArrayAccess все, что захочу? - person RonSper; 28.05.2015
comment
@RonSper Идея состоит в том, что вы должны установить null в свой массив и вернуть его вместо литерала. Не могу подтвердить это прямо сейчас, потому что я в отъезде :) - person Ja͢ck; 28.05.2015
comment
Спасибо за ваш комментарий, я смог заставить его работать с информацией, которую вы мне только что дали! - person RonSper; 28.05.2015

Мне удалось заставить приведенную ниже реализацию ArrayAccess работать идеально для моих нужд. Я надеюсь, что это поможет любому, у кого есть такие же проблемы, как и у меня.

Спасибо, @Jack, за то, что помог мне двигаться в правильном направлении!

class IssetArray implements \ArrayAccess {

    private $container = array();

    public function __construct() {}

    public function offsetSet($offset, $value) {
        if (is_null($offset)) {
            $this->container[] = $value;
        } else {
            $this->container[$offset] = $value;
        }
    }

    public function offsetExists($offset) {
        return isset($this->container[$offset]);
    }

    public function offsetUnset($offset) {
        unset($this->container[$offset]);
    }

    public function &offsetGet($offset) {
        if(!isset($this->container[$offset])) {
            $this->container[$offset] = null;
        }

        return $this->container[$offset];
    }
}
person RonSper    schedule 28.05.2015