Во-первых, цитата из руководства 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
, как показывает пример. Я исследовал это раньше, и я не думал, что это возможно, но, вернувшись к этому из-за неопределенности, я ( повторно ) обнаружил это упоминание в руководстве. Теперь я просто смущен.
Обновление . Когда я нажал « Опубликовать свой вопрос» , я понял, что это скорее всего относится к присваиванию ссылкой на другую переменную, например $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 просто запрещают это.
Это не работает с 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 :)
То, о чем говорится в руководстве, – это так называемые «косвенные модификации». Рассмотрим следующий сценарий:
$array = new ArrayObject; $array['foo'] = array(); $array['foo']['bar'] = 'foobar';
В приведенном выше скрипте $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)
, но практически это невозможно .
Кстати, ссылки трудно понять. Вы получите много неочевидного поведения, и это особенно верно для ссылок на элементы массива (у них есть специальные правила). Я бы рекомендовал вам просто избежать их вообще.