Referencje wewn▒trz konstruktora

Tworzenie referencji wewn▒trz konstruktora mo┐e prowadziµ do dziwnych efekt≤w. Ten rozdzia│ ma pom≤c w unikaniu takich problem≤w.

class Foo
{
    function Foo($nazwa)
    {
        // stworz referencje wewnatrz globalnej tablicy $globalref
        global $globalref;
        $globalref[] = &$this;
        // ustaw nazwΩ na przekazan▒ warto╢µ
        $this->ustawNazwe($nazwa);
        // i wy╢wietl j▒
        $this->wyswietlNazwe();
    }

    function wyswietlNazwe()
    {
        echo "<br>",$this->nazwa;
    }
	
    function ustawNazwe($nazwa)
    {
        $this->nazwa = $nazwa;
    }
}

Sprawd╝my, czy jest jaka╢ r≤┐nica pomiΩdzy $bar1, kt≤ry jest tworzony przy pomocy operatora przypisania =, a $bar2, kt≤ry zosta│ stworzony u┐ywaj▒c operatora referencji =&...

$bar1 = new Foo('ustawione w konstruktorze');
$bar1->wyswietlNazwe();
$globalref[0]->wyswietlNazwe();

/* wyj╢cie:
ustawione w konstruktorze
ustawione w konstruktorze
ustawione w konstruktorze */

$bar2 =& new Foo('ustawione w konstruktorze');
$bar2->wyswietlNazwe();
$globalref[1]->wyswietlNazwe();

/* wyj╢cie:
ustawione w konstruktorze
ustawione w konstruktorze
ustawione w konstruktorze */

Wydaje siΩ, ┐e nie ma ┐adnej r≤┐nicy, ale na prawdΩ jest jedna, i to bardzo istotna: $bar1 i $globalref[0] NIE s▒ referencjami, NIE s▒ t▒ sam▒ zmienna. Dzieje siΩ tak, poniewa┐ "new" nie zwraca domy╢lnie referencji, ale kopiΩ.

Notatka: Zwracanie kopii zamiast referencji nie powoduje utraty wydajno╢ci (od PHP 4 u┐ywane jest zliczanie referencji). Jednak┐e zazwyczaj lepiej jest pracowaµ poprostu z kopiami zamiast referencji, poniewaµ tworzenie referencji zabiera trochΩ czasu, podczas gdy tworzenie kopii obiekt≤w teoretycznie w og≤le nie zabiera czasu (chyba ┐e kt≤ra╢ z tych zmiennych jest du┐▒ tablic▒ lub obiektem i jedno z nich ulega zmianie, po czym tej samej zmianie ulegaj▒ pozosta│e zmienne; wtedy lepiej jest u┐yµ referencji do zmieniania ich r≤wnolegle).

Aby udowodniµ to, co zosta│o zapisane powy┐ej, przyjrzyjmy siΩ poni┐szemu programowi.

// teraz zmienimy nazwΩ. czego siΩ spodziewasz?
// mo┐esz siΩ spodziewaµ, ┐e i $bar1 i $globalref[0] zmieni▒ swoje nazwy...
$bar1->ustawNazwe('ustawiona z zewn▒trz');

// jak napisano powy┐ej, nic takiego siΩ nie stanie
$bar1->wyswietlNazwe();
$globalref[0]->wyswietlNazwe();

/* wyj╢cie:
ustawiona z zewn▒trz
ustawiona w konstruktorze */

// zobaczmy co siΩ dzieje z $bar2 i $globalref[1]
$bar2->ustawNazwe('ustawiona z zewn▒trz');

// na szczΩ╢cie ta zmienna nie zachowuje siΩ jak ta z poprzedniego przypadku
// s▒ to te same zmienne, z wiΩc $bar2->nazwa i $globalref[1]->nazwa s▒ tak┐e
// tymi samymi zmiennymi
$bar2->wyswietlNazwe();
$globalref[1]->wyswietlNazwe();

/* wyj╢cie:
ustawiona z zewn▒trz
ustawiona z zewn▒trz */

Ustatni przyk│ad. Postaraj siΩ go zrozumieµ/

class A
{
    function A($i)
    {
        $this->wartosc = $i;
        // domy╢l siΩ dlaczego nie potrzebujemy tutaj referencji
        $this->b = new B($this);
    }

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

    function wyswietlWartosc()
    {
        echo "<br>","klasa ",get_class($this),': ',$this->value;
    }
}


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

    function wyswietlWartosc()
    {
        echo "<br>","klasa ",get_class($this),': ',$this->a->value;
    }
}
// spr≤buj zrozumieµ dlaczego u┐ycie tu prostego kopiowania mo┐e powodowaµ
// nieporz▒dany efekt w linii uznaczonej znaczkiem '*'
$a =& new A(10);
$a->stworzRef();

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

$a->value = 11;

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

/*
wyj╢cie:
klasa A: 10
klasa B: 10
klasa B: 10
klasa A: 11
klasa B: 11
klasa B: 11
*/