A criação de referencias dentro de construtores podem levar à resultados confusos. Esta seção-tutorial tem o objetivo de ajudar você a evitar problemas.
<?php
class Foo {
function Foo($name) {
// cria uma referencia dentro do array global $globalref
global $globalref;
$globalref[] = &$this;
// altera o nome usando o valor passado
$this->setName($name);
// e envia ele ao output
$this->echoName();
}
function echoName() {
echo "<br />", $this->name;
}
function setName($name) {
$this->name = $name;
}
}
?>
Vamos verificar se existe alguma diferença entre $bar1, que foi criado utilizando o operador de cópia =, e $bar2, que foi criado utilizando o operador de referencia =&...
<?php
$bar1 = new Foo('alterado no construtor');
$bar1->echoName();
$globalref[0]->echoName();
/* saída:
alterado no construtor
alterado no construtor
alterado no construtor */
$bar2 =& new Foo('alterado no construtor');
$bar2->echoName();
$globalref[1]->echoName();
/* saída:
alterado no construtor
alterado no construtor
alterado no construtor */
?>
Aparentemente não existe diferença alguma, mas na realidade existe uma bem significante: $bar1 e $globalref[0] _NÃO_ são referencias, ou seja, NÃO são a mesma variável. Isso acontece porque "new" não returna uma referencia por padrão, mas sim uma cópia.
Para provar o que está escrito acima, vejamos o código abaixo.Nota: Não existe qualquer perda de performance (a partir do PHP 4 é utilizada contagem de referencias) ao retornar cópias ao invés de referencias. Na realidade é bem o contrário, normalmente é melhor simplesmente trabalhar com cópias ao invés de referências, pois a criação de referências leva um certo tempo, ao passo que cópias virtuais não (a não ser que nenhum deles seja um grande array ou objeto e, ao alterar um deles, os outros devem ser alterados subsequentemente, nesse caso seria sábio utilizar referências para que todos sejam alterados simultâneamente).
<?php
// agora nós iremos alterar o nome. o que você espera que aconteça?
// você pode achar que tanto $bar1 e $globalref[0] tenham seus nomes alterados...
$bar1->setName('alterado de fora');
// como dito antes, este não é o caso.
$bar1->echoName();
$globalref[0]->echoName();
/* saída:
alterado de fora
alterado no construtor */
// vamos ver a diferença no caso de $bar2 e $globalref[1]
$bar2->setName('alterado de fora');
// felizmente eles não são apenas iguais, mas sim as mesmas variáveis
// assim $bar2->name e $globalref[1]->name são também a mesma coisa
$bar2->echoName();
$globalref[1]->echoName();
/* saída:
alterado de fora
alterado de fora */
?>
Outro exemplo final, para tentarmos entender isso tudo.
<?php
class A {
function A($i) {
$this->value = $i;
// tente descobrir o motivo de não precisarmos de uma referencia aqui
$this->b = new B($this);
}
function createRef() {
$this->c = new B($this);
}
function echoValue() {
echo "<br />","class ",get_class($this),': ',$this->value;
}
}
class B {
function B(&$a) {
$this->a = &$a;
}
function echoValue() {
echo "<br />","class ",get_class($this),': ',$this->a->value;
}
}
// tente entender por que utilizar uma cópia simples levaria um
// resultado inesperado na linha marcada com um *
$a =& new A(10);
$a->createRef();
$a->echoValue();
$a->b->echoValue();
$a->c->echoValue();
$a->value = 11;
$a->echoValue();
$a->b->echoValue(); // *
$a->c->echoValue();
?>
O exemplo acima irá imprimir:
class A: 10 class B: 10 class B: 10 class A: 11 class B: 11 class B: 11