PHP Velho Oeste 2024

Referencias dentro do construtor

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.

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).

Para provar o que está escrito acima, vejamos o código abaixo.
<?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 {
    function 
A($i) {
        
$this->value $i;
        
// tente descobrir o motivo de não precisarmos de uma referencia aqui
        
$this->= new B($this);
    }

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

    function 
echoValue() {
        echo 
"<br />","class ",get_class($this),': ',$this->value;
    }
}


class 
{
    function 
B(&$a) {
        
$this->= &$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

add a note add a note

User Contributed Notes

There are no user contributed notes for this page.
To Top