ArrayAccess в PHP - назначение смещения по ссылке - программирование
Подтвердить что ты не робот

ArrayAccess в PHP - назначение смещения по ссылке

Во-первых, цитата из руководства ole 'на ArrayAccess::offsetSet():

Эта функция не вызывается в назначениях по ссылке и в противном случае косвенные изменения в размерах массива, перегруженные с помощью ArrayAccess (косвенные в том смысле, что они выполняются не путем изменения размера напрямую, а путем изменения субразмера или суб-свойства или назначения размера массива ссылкой на другую переменную). Вместо этого вызывается ArrayAccess:: offsetGet(). Операция будет успешной только в том случае, если этот метод возвращается по ссылке, что возможно только с PHP 5.3.4.

Я немного смущен этим. Похоже, что это предполагает, что (начиная с 5.3.4) можно определить offsetGet() для возврата по ссылке в классе реализации, таким образом обрабатывая назначения по ссылке.

Итак, теперь тестовый фрагмент:

(игнорируйте отсутствие проверки и проверку isset())

class Test implements ArrayAccess
{
    protected $data = array();

    public function &offsetGet($key)
    {
        return $this->data[$key];
    }

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

    public function offsetExists($key) { /* ... */ }

    public function offsetUnset($key) { /* ... */ }

}

$test = new Test();

$test['foo'] = 'bar';
$test['foo'] = &$bar; // Fatal error: Cannot assign by reference to
                      // overloaded object in

var_dump($test, $bar);    

Хорошо, так что это не сработает. Тогда, к чему относится это руководство?

Причина
Я хотел бы разрешить присваивание посредством ссылки через оператор массива объекту, реализующему ArrayAccess, как показывает пример. Я исследовал это раньше, и я не думал, что это возможно, но, вернувшись к этому из-за неопределенности, я (повторно) обнаружил это упоминание в руководстве. Теперь я просто смущен.


Обновить. Когда я нажал Post Your Question, я понял, что это скорее всего относится к присваиванию ссылочной переменной другой, например $bar = &$test['foo'];. Если это дело, то извиняйтесь; хотя зная, как, если это вообще возможно, назначить по ссылке на перегруженный объект, было бы здорово.


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

isset($obj[$key]);       // $obj->has_data($key);

$value = $obj[$key];     // $obj->get_data($key);

$obj[$key] = $value;     // $obj->set_data($key, $value);

$obj[$key] = &$variable; // $obj->bind_data($key, $variable);

// also, flipping the operands is a syntactic alternative
$variable = &$obj[$key]; // $obj->bind_data($key, $variable);

unset($obj[$key]);       // $obj->remove_data($key);

Что касается has, get, set и remove go, это не проблема с поддерживаемыми методами ArrayAccess. Функциональность привязки - это то место, где я нахожусь в убытке, и я начинаю считать, что ограничения ArrayAccess и PHP просто запрещают это.

4b9b3361

Ответ 1

Это не работает с ArrayAccess, вы можете добавить себе публичную функцию, которая позволяет вам установить ссылку на смещение (конечно, это выглядит иначе, чем синтаксис массива, поэтому на самом деле это не достаточный ответ):

class Test implements ArrayAccess{

    protected $_data = array();

    public function &offsetGet($key){
        return $this->_data[$key];
    }

    ... 

    public function offsetSetReference($key, &$value)
    {
        $this->_data[$key] = &$value;
    }
}

$test = new Test();
$test['foo'] = $var = 'bar';
$test->offsetSetReference('bar', $var);    
$var = 'foo';    
echo $test['bar']; # foo    
$alias = &$test['bar'];    
$alias = 'hello :)';    
echo $var; # hello :)

Вероятно, такая функция была забыта, когда ArrayAccess был впервые реализован.

Изменить: Передайте его как "ссылочное задание":

class ArrayAccessReferenceAssignment
{
    private $reference;
    public function __construct(&$reference)
    {
        $this->reference = &$reference;
    }
    public function &getReference()
    {
        $reference = &$this->reference;
        return $reference;
    }
}


class Test implements ArrayAccess{
    ...
    public function offsetSet($key, $value){
        if ($value instanceof ArrayAccessReferenceAssignment)
        {
           $this->offsetSetReference($key, $value->getReference());
        }
        else
        {
           $this->_data[$key] = $value;
        }
    }

Что тогда работает безупречно, потому что вы внедрили его. Это, вероятно, более приятное взаимодействие, чем более явный вариант offsetSetReference выше:

$test = new Test();
$test['foo'] = $var = 'bar';
$test['bar'] = new ArrayAccessReferenceAssignment($var);

$var = 'foo';
echo $test['bar']; # foo
$alias = &$test['bar'];
$alias = 'hello :)';
echo $var; # hello :)

Ответ 2

К чему относится руководство, относятся так называемые "косвенные модификации". Рассмотрим следующий script:

$array = new ArrayObject;
$array['foo'] = array();
$array['foo']['bar'] = 'foobar';

В приведенном выше script $array['foo'] = array(); будет запускаться offsetSet('foo', array()). $array['foo']['bar'] = 'foobar'; с другой стороны вызовет offsetGet('foo'). Почему так? Последняя строка будет оцениваться примерно так же под капотом:

$tmp =& $array['foo'];
$tmp['bar'] = 'foobar';

Итак, $array['foo'] сначала извлекается ref, а затем изменяется. Если ваш offsetGet возвращает ref, это будет успешным. Если нет, вы получите некоторую косвенную ошибку модификации.


То, что вы хотите, с другой стороны, является абсолютно противоположным: не извлекать значение по ссылке, а назначать его. Теоретически это потребовало бы подписи offsetSet($key, &$value), но практически это просто невозможно.

Кстати, ссылки трудно понять. Вы получите много неочевидного поведения, и это особенно верно для ссылок на элементы массива (у них есть специальные правила). Я бы рекомендовал вам просто избежать их вообще.