PHP 5.6: ArrayAccess: функция isset вызывает offsetGet и вызывает неопределенное уведомление об индексе

Я написал простой класс PHP, который реализует интерфейс ArrayAccess:

class MyArray implements ArrayAccess
{
    public $value;

    public function __construct($value = null)
    {
        $this->value = $value;
    }

    public function &offsetGet($offset)
    {
        var_dump(__METHOD__);

        if (!isset($this->value[$offset])) {
            throw new Exception('Undefined index: ' . $offset);
        }

        return $this->value[$offset];
    }

    public function offsetExists($offset)
    {
        var_dump(__METHOD__);

        return isset($this->value[$offset]);
    }

    public function offsetSet($offset, $value)
    {
        var_dump(__METHOD__);

        $this->value[$offset] = $value;
    }

    public function offsetUnset($offset)
    {
        var_dump(__METHOD__);

        $this->value[$offset] = null;
    }
}

Он работает нормально в PHP 7, но проблема в PHP 5.6 и HHVM.

Если я вызываю функцию isset() по неопределенному индексу PHP вызовет offsetGet() вместо offsetExists() что приведет к Undefined index уведомление.

В PHP 7 это вызывает offsetGet() только если offsetExists() возвращается true, так что ошибки нет.

Я думаю, что это связано с ошибкой PHP 62059.

Код доступен в 3V4L, так что вы можете увидеть, что не так. Я добавил еще несколько вызовов отладки и выбросил исключение, если индекс не определен, потому что уведомления не отображаются в 3V4L: https://3v4l.org/7C2Fs

Не должно быть никакого уведомления, иначе тесты PHPUnit не пройдут. Как я могу исправить эту ошибку?

2 ответа

Решение

Похоже, что это ошибка PHP в старых версиях PHP и HHVM. Поскольку PHP 5.6 больше не поддерживается, эта ошибка не будет исправлена.

Быстрое решение состоит в том, чтобы добавить дополнительную проверку в методе offsetGet() и вернуться null если индекс не определен:

class MyArray implements ArrayAccess
{
    public $value;

    public function __construct($value = null)
    {
        $this->value = $value;
    }

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

        return $this->value[$offset];
    }

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

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

    public function offsetUnset($offset)
    {
        $this->value[$offset] = null;
    }
}

Смотрите код в 3V4L и комментарии zerkms( первый, второй, третий).

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

public function __construct($value =[]){
    $this->value = $value;
}

вместо:

public function __construct($value = null){
$this->value = $value;
}
Другие вопросы по тегам