Las referencias al interior del constructor

Crear referencias al interior del constructor puede llevar a resultados confusos. Esta sección tipo-tutorial le ayuda a evitar problemas.

<?php
class Foo {
    function
Foo($nombre) {
        
// crear una referencia al interior de la matriz global $refglobal
        
global $refglobal;
        
$refglobal[] = &$this;
        
// definir el nombre al valor pasado
        
$this->definirNombre($nombre);
        
// e imprimirlo
        
$this->imprimirNombre();
    }

    function
imprimirNombre() {
        echo
"<br />", $this->nombre;
    }

    function
definirNombre($nombre) {
        
$this->nombre = $nombre;
    }
}
?>

Revisemos si existe una diferencia entre $bar1, que ha sido creado usando el operador de copia = y $bar2 que ha sido creado usando el operador de referencia =&...

<?php
$bar1
= new Foo('definido en el constructor');
$bar1->imprimirNombre();
$refglobal[0]->imprimirNombre();

/* salida:
definido en el constructor
definido en el constructor
definido en el constructor */

$bar2 =& new Foo('definido en el constructor');
$bar2->imprimirNombre();
$refglobal[1]->imprimirNombre();

/* salida:
definido en el constructor
definido en el constructor
definido en el constructor */
?>

Aparentemente no hay ninguna diferencia, pero en realidad hay una bastante importante: $bar1 y $refglobal[0] _NO_ son referenciados, NO son la misma variable. Esto se debe a que "new" no devuelve una referencia por defecto, en su lugar devuelve una copia.

Nota: No existe una pérdida de rendimiento (ya que desde PHP 4 se usa el conteo de referencias) al devolver copias en lugar de referencias. Al contrario, con frecuencia es mejor trabajar simplemente con copias en lugar de referencias, ya que crear referencias toma cierto tiempo mientras que crear copias prácticamente no toma nada de tiempo (a menos que ninguna de ellas sea una matriz u objeto grande y una de ellas se modifique y luego las otras subsecuentemente, entonces sería buena idea usar referencias para modificarlas todas al mismo tiempo).

Para probar lo que se dice más arriba, veamos el siguiente código.

<?php
// ahora cambiaremos el nombre. que espera que pase?
// puede que espere que tanto $bar1 como $refglobal[0] cambien sus nombres...
$bar1->definirNombre('definido desde afuera');

// como se ha mencionado antes, ese no es el caso.
$bar1->imprimirNombre();
$refglobal[0]->imprimirNombre();

/* salida:
definido desde afuera
definido en el constructor */

// veamos que cambia entre $bar2 y $refglobal[1]
$bar2->definirNombre('definido desde afuera');

// por suerte, no solo son iguales, son la misma variable, de modo que
// $bar2->nombre y $refglobal[1]->nombre son el mismo tambien
$bar2->imprimirNombre();
$refglobal[1]->imprimirNombre();

/* salida:
definido desde afuera
definido desde afuera */
?>

Otro ejemplo final, intente entenderlo.

<?php
class A {
    function
A($i) {
        
$this->valor = $i;
        
// intente descubrir porque no necesitamos una referencia aqui
        
$this->b = new B($this);
    }

    function
crearRef() {
        
$this->c = new B($this);
    }

    function
echoValor() {
        echo
"<br />","clase ",get_class($this),': ',$this->valor;
    }
}


class
B {
    function
B(&$a) {
        
$this->a = &$a;
    }

    function
echoValor() {
        echo
"<br />","clase ",get_class($this),': ',$this->a->valor;
    }
}

// intente entender porque usar una simple copia produciria
// un resultado no deseado en la linea marcada con *
$a =& new A(10);
$a->crearRef();

$a->echoValor();
$a->b->echoValor();
$a->c->echoValor();

$a->valor = 11;

$a->echoValor();
$a->b->echoValor(); // *
$a->c->echoValor();

?>

El resultado del ejemplo seria:

clase A: 10
clase B: 10
clase B: 10
clase A: 11
clase B: 11
clase B: 11

Hosting by: hurra.com
Generated: 2007-01-26 18:00:52