2013-05-20 4 views
19

Я понимаю, почемуИгра со ссылками

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$c['ID'] = 37; 
echo $a['ID']."\n"; 
echo $b."\n"; 
echo $c['ID']."\n"; 

выходы 37, 42, 37

в то время как

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$b = 37; 
echo $a['ID']."\n"; 
echo $b."\n"; 
echo $c['ID']."\n"; 

выходы 37, 37, 37

В обоих случаях $b является ссылка на $a['ID'], а $c - указатель того же объекта, что и $a.

Когда $b изменяет $a['ID'] и $c['ID'] изменения, поскольку назначение $b изменяет значение, на который ссылается $a['ID'].

Когда $c['ID'] изменения, новый INT присваивается $a['ID'], $b не ссылается $a['ID'] больше.

Но это чешется мне

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$c['ID'] &= 0; 
$c['ID'] |= 37; 
echo $a['ID']."\n"; 
echo $b."\n"; 
echo $c['ID']."\n"; 

(выходы 37, 37, 37)

Является ли это определено поведение? я не вижу ничего о том, что в документации ...

+3

Да, я не думаю, что это задокументировано. В руководстве указано, что ++ и - выполнить косвенную модификацию и, таким образом, вызывать 'ArrayAccess :: offsetGet()', а не 'ArrayAccess :: offsetSet()'. Для согласованности я полагаю, что + =, - =, и двоюродные братья были реализованы таким же образом. – cleong

+0

@cleong Я не могу найти его в документах. Не могли бы вы указать, где? – webmaster777

ответ

3

Давайте возьмем этот код в качестве основы: (refcounting documentation)

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 

xdebug_debug_zval('a'); 
xdebug_debug_zval('b'); 
xdebug_debug_zval('c'); 

Это дает:

a: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=2, is_ref=1),int 42 
b: 
(refcount=2, is_ref=1),int 42 
c: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=2, is_ref=1),int 42 

Как вы говорите, : $a является объектом, $b является ссылкой $a['ID'] ($a['ID'] и $b: refcount=2, is_ref=1) и $ с is copy as a reference (since PHP5), так что $ с является отнесение $ а (это теперь тот же объект: refcount=2, is_ref=0)


Если мы: $c['ID'] = 37;

Получаем:

a: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=1, is_ref=0),int 37 
b: 
(refcount=1, is_ref=0),int 42 
c: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=1, is_ref=0),int 37 

$c['ID'] присваивается новый int so => ​​

$b становится независимым ()и is_ref=0), а также $a['ID'] и $c['ID']

НО, как $c и $a зависят, $a['ID'] и $c['ID'] принимают одинаковое значение 37.


Теперь, давайте рассмотрим базовый код, и мы делаем: $c['ID'] &= 0;

UPDATE: Неожиданно мы получаем:

a: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=2, is_ref=1),int 0 
b: 
(refcount=2, is_ref=1),int 0 
c: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=2, is_ref=1),int 0 

вместо: (если: $c['ID'] = $c['ID'] & 0;)

a: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=1, is_ref=0),int 0 
b: 
(refcount=1, is_ref=0),int 42 
c: 
(refcount=2, is_ref=0), 
object(ArrayObject)[1] 
    public 'ID' => (refcount=1, is_ref=0),int 0 

ArrayObject реализует ArrayAccess так:

Как сказано в комментарии и documented here:

прямая модификация является тот, который заменяет полностью значение размерности массива, как и в $ OBJ [6] = 7. Косвенная модификация, с другой стороны, только изменяет часть измерения или пытается присвоить размерность ссылкой на другую переменную, как в $ obj [6] [7] = 7 или $ var = & $ obj [ 6]. Приращения с ++ и декременты с помощью - также реализуются таким образом, что требуется косвенная модификация.

Возможный ответ:

"Комбинированный оператор (+ =, - =, & =, | =) можно было бы работал таким же образом (косвенные изменения.)":

refcount и is_ref не влияют, поэтому (в нашем случае) изменяются значения всех связанных переменных. ($c['ID'] =>$a['ID'] =>$b)

+1

И вы не видите, как это _unexpected_ по сравнению с примером 1? – webmaster777

+1

Действительно, вы должны попробовать этот код. ('$ c ['ID'] = $ c ['ID'] & 0') – webmaster777

+0

@ webmaster777, действительно ... я был не прав – antoox

2

Это более или менее определенные (но иногда недокументированные) поведение, в основном потому, что $a не является array но ArrayObject

Давайте посмотрим на ваш третий фрагмент кода. первый:

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$c['ID'] &= 0; 

Последнее задание переводится:

$tmp = &$c->offsetGet('ID'); 
$tmp &= 0; // or: $tmp = $tmp & 0; 

Точка Вынос здесь называется толькоoffsetGet() и возвращает ссылку к $c['ID'], как отмечено в this comment. Потому что offsetSet() не вызывается, также изменяется значение $b.

Btw, приращение (++) и оператор декремента (-) работают аналогичным образом, не вызывается offsetSet().

Различия

Это отличается от первого примера:

$a = new ArrayObject(); 
$a['ID'] = 42; 
$b = &$a['ID']; 
$c = $a; 
$c['ID'] = 37; 

Последнее утверждение имеет следующий эквивалент:

$c->offsetSet('ID', 37); 

Перед тем, как новое значение присвоенного $c['ID'] , предыдущее значение равно unset(); поэтому $b является единственной переменной, все еще удерживающей до 42.

Доказательство этого поведения можно увидеть при использовании объекта вместо цифр:

class MyLoggerObj 
{ 
     public function __destruct() 
     { 
       echo "Destruct of " . __CLASS__ . "\n"; 
     } 
} 

$a = new ArrayObject(); 
$a['ID'] = new MyLoggerObj(); 
$a['ID'] = 37; 

echo $a['ID']."\n"; 

Output:

Destruct of MyLoggerObj 
37 

Как вы можете видеть, деструктор вызывается MyLoggerObj; это потому, что в этом случае переменная больше не удерживается.

Bonus

Если вы пытаетесь выяснить, когда offsetGet() и offsetSet() называются путем расширения ArrayObject вы будете разочарованы, чтобы узнать, что вы не можете правильно имитировать mixed &offsetGet($key);. Фактически, это меняет поведение ArrayObject таким образом, что невозможно доказать поведение этого класса.

+1

Все еще не убежден: P, Как получилось, что b не обновляется в примере 1 ? Согласно вашему _expected_ поведению offsetSet, он меняет ссылку (косвенную модификацию), но тогда b также должно быть 37. – webmaster777

+0

Я могу только догадываться, что здесь есть другая семантика, но как только я попаду на компьютер, я придаю этому аспекту некоторое внимание. –

+1

Это приведет к тому, что результат Ex.3 будет равен 37,42,37 (так как ссылка $ c ['id'] не соответствует вашим стандартам, а после этого установлена ​​в 0). – webmaster777