2012-10-04 22 views
6

El código siguiente ilustra el extraño comportamiento de las referencias de PHP:¿Alguien puede explicar la diferencia entre estos 2 usos de referencia en PHP?

resultados
<?php 

function this_works() 
{ 
    $root = array('name'=>'root', 'children'=>array()); 
    $level_1 = array('name'=>'level_1', 'children'=>array()); 
    $item1 = array('name'=>'level_2_1', 'children'=>array()); 
    $item2 = array('name'=>'level_2_2', 'children'=>array()); 

    $croot = &$root; 

    $croot['children'][] = &$level_1; 

    $croot = &$level_1; 

    $croot['children'][] = &$item1; 
    $croot['children'][] = &$item2; 

    $croot = &$root; 

    print_r($croot); 
}  

function this_fails() 
{  
    $root = array('name'=>'root', 'children'=>array()); 
    $level_1 = array('name'=>'level_1', 'children'=>array()); 
    $item1 = array('name'=>'level_2_1', 'children'=>array()); 
    $item2 = array('name'=>'level_2_2', 'children'=>array()); 
    $croot = &$root; 

    $stack = array(); 

    $croot['children'][] = &$level_1; 
    $crootref = &$croot; 

    array_push($stack, $crootref); 

    $croot = &$level_1; 

    $croot['children'][] = &$item1; 
    $croot['children'][] = &$item2; 

    # this works, assignment below - doesn't... WHY? 
    #$x = array_pop($stack); 
    #var_dump($x); 

    $croot = array_pop($stack); 

    print_r($croot); 
}  

this_works(); 
echo "------------------\n"; 
this_fails(); 

?> 

Primera función proporciona esperaba, mientras que la segunda falla y afirmaciones acerca de la repetición en bucle:

Array 
(
    [name] => root 
    [children] => Array 
     (
      [0] => Array 
       (
        [name] => level_1 
        [children] => Array 
         (
          [0] => Array 
           (
            [name] => level_2_1 
            [children] => Array 
            (
            ) 

           ) 

          [1] => Array 
           (
            [name] => level_2_2 
            [children] => Array 
             (
             ) 

           ) 

          ) 

       ) 

     ) 

) 
------------------ 
Array 
(
    [name] => root 
    [children] => Array 
     (
      [0] => Array 
       (
        [name] => root 
        [children] => Array 
*RECURSION* 
       ) 

    ) 

) 

Lo que es extraño, es que si en la segunda función, la variable intermedia se usará para obtener el valor de la pila, los resultados vuelven a ser correctos. No entiendo lo que está pasando. ¿Cómo obtengo el elemento raíz como como un hijo de sí mismo muchas veces debido a una asignación?

Originalmente, tenía que construir el árbol de XML (utilizando analizador SAX) y intented tener 'raíz actual' que apunta al nodo de árbol en el nivel y la tendencia actual/pop a/de pila y añadir niño elementos a él, pero, sorprendentemente, no pude implementar este esquema debido a problemas demostrados por dos funciones anteriores.

Entonces, ¿qué hay de malo con ese enfoque?

Respuesta

0

Una imagen worths 1000 palabras. Tardó un tiempo en comprender qué está pasando exactamente.

Tuve que usar Xdebug para volcar datos internos de y ver los recuentos y los efectos de la copia en escritura.

El problema con el código en la primera entrada es que la asignación

$ Croot = array_pop ($ pila);

cuando Croot es 'level_1' se realiza mediante la copia, elementos es decir de Croot (igual que level_1) están poblados con datos de pila y Croot no es la misma que la raíz original después de esta operación.

La imagen explicará mejor.

PHP variables in memory

0

Es por la forma extraña en que PHP hace referencias dentro de las matrices. Hacer una asignación normal (no por referencia) con una referencia en el lado derecho generalmente no convierte el lado izquierdo en una referencia, pero las referencias dentro de las matrices se conservan en las asignaciones, incluso sin el operador de referencia.

He comentado tu código a continuación para ayudar a explicar lo que está sucediendo, espero haber explicado esto correctamente, es la 1am y me estoy cansando.

$root = array('name'=>'root', 'children'=>array()); 
$level_1 = array('name'=>'level_1', 'children'=>array()); 
// $croot and $root now point to the same variable 
$croot = &$root; 

$stack = array(); 

$croot['children'][] = &$level_1; 
// $crootref, $croot and $root all now point to the same variable 
$crootref = &$croot; 
// $stack[0], $crootref, $croot and $root all now point to the same variable. 
// $stack[0]['children'][0], $level_1, $croot['children'][0] point to the same variable 
array_push($stack, $crootref); 
// $croot, $level_1 and $stack[0]['children'][0] now point to the same variable 
// Infinite loop is caused as $stack[0]['children'][0] is now an alias for $croot 
// which contains $croot['children'][0] which is an alias for $stack[0]['children'][0] 
// which is an alias for $croot which contains.... 
$croot = &$level_1; 
0

Solo una solución, usando unset() en lugar de array_pop(). Y debido a que cuando se utiliza array_pop() se resseted el puntero de la matriz de entrada yo uso reset() para simular los mismos resultados, aquí van:

<?php 

function this_fails_fixed() 
{  
    $root = array('name'=>'root', 'children'=>array()); 
    $level_1 = array('name'=>'level_1', 'children'=>array()); 
    $item1 = array('name'=>'level_2_1', 'children'=>array()); 
    $item2 = array('name'=>'level_2_2', 'children'=>array()); 
    $croot = &$root; 

    $stack = array(); 

    $croot['children'][] = &$level_1; 
    $crootref = &$croot; 

    array_push($stack, $crootref); 

    $croot = &$level_1; 

    $croot['children'][] = &$item1; 
    $croot['children'][] = &$item2; 

    unset($croot[count($croot)-1]); 
     reset($croot); 

    print_r($croot); 
}  

this_fails_fixed(); 

?> 
Cuestiones relacionadas