2013-09-28 3 views
2

Я пытаюсь понять ссылку PHP, но я нашел проблему при работе с цепочками ссылок.Связанные ссылки в PHP

class A 
{ 
    public $val; 

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

$values = array(
    'a' => new A('a'), 
    'b' => new A('b'), 
    'c' => new A('c') 
); 

$values['a'] = &$values['b']; 
$values['b'] = &$values['c']; 

Возврат:

array(
'a' => new A('b'), 
'b' => new A('c'), 
'c' => new A('c') 
) 

Почему 'Вал' в объекте А для ключа 'а' 'Ъ'? Я ожидал, что значение будет «c». Спасибо

+0

Я думаю, что сначала нужно установить 'b' и' c', иначе для каждой ссылки не нужно ничего. Попробуйте инициализировать их в первой строке с фиктивными значениями? – halfer

+1

Вы не можете просто изменить свой вопрос таким решительным образом. Любой комментарий или ответ сразу же ошибочны или, по крайней мере, запутывают будущих посетителей. –

+0

На самом деле, я думаю, что ваш первый вопрос был в любом случае лучше: смешивание объектов и эталонных переменных, подобных этому, просто смущает вопрос, но основной принцип тот же. – IMSoP

ответ

1

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

$a1 = new A('a'); 
$a2 = $a1; 

является похожа на:

$b1 = new A('a'); 
$b2 = &$b1; 

В пути, что:

$a2->val = 1; // $a1->val is now equal to 1, because $a1 and $a2 are pointing 
       // to the same instance 
$b2->val = 1; // $b1->val is now equal to 1, because $b2 points to $b1 

Существует тонкое различие, хотя:

$a1 = 1; // $a2 still points to A object 
$b1 = 1; // $b2 still points to $a1 which points to number 1, 
     // therefore $b2 == 1 

Также он работает по-разному с массивами, потому что назначение массива всегда связано с копированием значений.

Если вы хотите, чтобы понять, что происходит в вашем примере, давайте посмотрим:

Так что ваш исходный массив заключается в следующем:

$values = array(
    'a' => new A('a'), 
    'b' => new A('b'), 
    'c' => new A('c') 
); 

Давайте посмотрим, что происходит в там шаг за шагом :

$values['a'] = &$values['b']; // $values['a'] is now reference to new A('b') 
           // that means your original new A('a') is now 
           // lost 

$values['b'] = &$values['c']; // $values['b'] is now reference to new A('c') 
           // stored under 'c' key, that means $values['b'] 
           // is now equal to $values['c'] ; note that this is 
           // different than $b2 = &$b1; from the above example 
           // since we use an array and not bare variables 
           // the $values['a'] points to value stored under 
           // the 'b' key, but we replace the 'b' key value 
           // as opposed to giving it a new value; 
           // "Array assignment always involves value copying." 

// So you ended up with this result: 
array(
    'a' => new A('b'), 
    'b' => new A('c'), // this is actually reference to 'c', just wrote new A() 
        // to keep this part consistent with your question's 
        // formatting 
    'c' => new A('c') 
) 
+0

Остерегайтесь таких утверждений, как «объекты ссылаются по умолчанию» - объекты имеют * дополнительный * уровень косвенности, который не зависит от задания задания ('= &'), и между ними существуют важные различия. – IMSoP

+0

Право, я обновил свой ответ – adamziel

1

$values['b'] не было, когда вы назначили его $values['a']. Если изменить порядок, он работает, как вы описываете:

~$ php -a 
php > $values = array(); 
php > $values['c'] = 1; 
php > $values['b'] = &$values['c']; 
php > $values['a'] = &$values['b']; 
php > print_r($values); 
Array 
(
    [c] => 1 
    [b] => 1 
    [a] => 1 
) 
+0

Извините, я описал это неправильно. Я переписал свой ответ. – user1518183

+0

Это интуитивно разумно, но неверно: это не несуществование '$ values ​​['b']', это проблема, а тот факт, что он переназначен на другое значение, оставляя значения '$ [ 'a'] 'все еще указал на старое значение. – IMSoP

+0

Ключевое недоразумение в том, что существует цепочка для перемещения - не рисуйте стрелки, идущие от переменной к переменной, стрелки изображения, указывающие внутрь от множества переменных до одного и того же места. – IMSoP

0

Примечание: этот ответ на the question as originally asked. Новый вопрос несколько иной, хотя использование объектов в этом случае не меняет фундаментального поведения.

Ссылки PHP не могут быть «закольцованы» - оператор =& не похож на механизм ссылки/де-ссылки указателя, он связывает две (или более) переменные вместе. Но не важно, что ни одно имя переменной не является более «реальным», чем другое. Это как (или все) ссылки на безымянное значение.

Например, $foo =& $bar принимает значение, на который указывает $bar и также точек $foo при этом значении. Если вы затем назначаете любую переменную (например, $foo = 42; или $bar = 42;), значение обновляется. Некоторые действия, такие как unset($bar), действуют на переменную , а не значение, поэтому они влияют только на эту конкретную переменную, а не на ссылочное значение.

Вы можете увидеть это в действии, выполнив это: $foo = 42; $bar =& $foo; unset($foo); echo $bar; Обратите внимание, что $foo было «оригинальным» именем для значения, но даже после его отмены значение все еще существует. Затем вы можете повторно использовать переменную $foo для совершенно другого значения, не влияя на $bar.

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

В коде, вы делаете это: (. В качестве технических подробностей, что я называю здесь «ценность» является структурой в рамках внутреннего РНР называется zval)

// 1: Make $values['a'] point to the value currently pointed at by $values['b'] 
$values['a'] =& $values['b']; 
// 2: Make $values['b'] point to the value currently pointed at by $values['c'] 
// the previous value is now only pointed to by $values['a'] 
$values['b'] =& $values['c']; 
// 3: Set the value shared by $values['b'] and $values['c'] to 1 
$values['c'] = 1; 

Edit для обновления вопроса:

объектов могут показаться, чтобы сделать это более сложным, так как у них есть дополнительного уровня косвенности, который работает почти Точно так же: $foo = new A('a'); $bar = $foo; все еще создает два значения (zval s), но эти значения оба указателя на тот же объект .

В вашем случае, это не имеет никакого значения, так как вы все еще используете ссылочное назначение (=&) - и если вы не делали, вы получите тот же результат:

// 1: Make the value of $values['a'] point to the object currently pointed at by the value of $values['b'] 
$values['a'] = $values['b']; 
// 2: Make the value of $values['b'] point to the object currently pointed at by the value of $values['c'] 
// the previous object is now only pointed to by the value of $values['a'] 
$values['b'] = $values['c']; 

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

// Change the object: updates the object shared by $values['b'] and $values['c'] 
$values['c']->value = 42; 
// Change the value: will only update $values['b'] if =& was used to tie them together 
$values['c'] = 42; 
// Change the variable: will never affect $values['b'] 
unset($values['c']); 
Смежные вопросы