Hlavní navigace

PHP v objetí objektů (5)

Michal Burda

V předposledním pokračování seriálu o objektově orientovaném programování v PHP si ukážeme jednu záludnost, která na programátory číhá v nevinně vyhlížejícím operátoru přiřazení (=).

Objekty a operátor přiřazení

Objekty vystupující jako argument operátoru přiřazení ( =) jsou podle mě další velkou nepříjemností, která vás v PHP čeká. Narozdíl od jiných programovacích jazyků v PHP příkazem

$promenna = $objekt; 

přiřadíte do proměnné $promenna kopii (!) objektu uloženého pod proměnnou $objekt. Tento fakt se musí obcházet použitím odkazů (operátoru &). Dlouho jsem si na tuto skutečnost nemohl zvyknout a byla příčinou mnoha nechutných chyb. Pro lepší pochopení problému uvedu příklad:

class NejakaTrida {
  var $hodnota;

  function NejakaTrida($h) {
    $this->hodnota = $h;
  }
}


function zobraz() {
  global $a, $b, $c;
  print '<p>';
  print 'Hodnota $a je ' . $a->hodnota . '<BR>';
  print 'Hodnota $b je ' . $b->hodnota . '<BR>';
  print 'Hodnota $c je ' . $c->hodnota . '<BR>';
}


$a = new NejakaTrida('AAA');
$b = $a;
$c =& $a;
zobraz();

$b->hodnota = 'BBB';
zobraz();

$c->hodnota = 'CCC';
zobraz(); 

Výsledkem kódu bude:

Hodnota $a je AAA
Hodnota $b je AAA
Hodnota $c je AAA

Hodnota $a je AAA
Hodnota $b je BBB
Hodnota $c je AAA

Hodnota $a je CCC
Hodnota $b je BBB
Hodnota $c je CCC 

Pokud k přiřazení objektu do proměnné použijeme příkaz typu

$b = $a; 

uloží se do $b kopie objektu $a. Vznikne tak další samostatný objekt, který nemá s prvním nic společného (až na třídu, ke které oba patří).

K tomu, abyste do nové proměnné ( $c) uložili odkaz na tentýž objekt ( $a), musíte použít konstrukci

$c =& $a; 

Od této chvíle je $c jen jiné jméno pro objekt uložený v $a  – obě proměnné odkazují na tentýž objekt.

Často můžete potřebovat metodu, která jako argument požaduje odkaz na objekt. Potom před název argumentu v hlavičce musíte umístit znak  &:

function ptakovina(&$odkaz_na_objekt) {
  # ...
} 

Malá ukázka použití v praxi:

/*
Kontejnerová třída, která ukládá do pole různé objekty
a na požádání volá jejich metody zobraz()
*/
class Kontejner {
  var $polozky;

  # vlož odkaz na objekt do kontejneru
  function vloz(&$obj) {
    $this->polozky[] =& $obj;
  }

  # zobraz všechny objekty v kontejneru
  function zobraz() {
    for ($x = 0; $x < count($this->polozky); $x++) {
      $this->polozky[$x]->zobraz();
    }
  }
}


/*
Jednoduchá třída implementovaná proto, abychom
měli do kontejneru co vkládat
*/
class Odstavec {
  var $text;

  # konstruktor
  function Odstavec($t = '') {
    $this->text = $t;
  }

  # zobrazovací metoda
  function zobraz() {
    print '<P>Text odstavce: ' . $this->text . '</P>';
  }
}


$stranka = new Kontejner();

$a = new Odstavec('Odstavec 1');
$b = new Odstavec('Odstavec 2');
$c = new Odstavec();

$stranka->vloz($a);
$stranka->vloz($b);
$stranka->vloz($c);

$c->text = 'Odstavec 3';

$stranka->zobraz(); 

Vynecháním všech & v metodě vloz() třídy Kontejner byste si zadělali na nepříjemnou chybu. Místo očekávaného výstupu:

Text odstavce: Odstavec 1
Text odstavce: Odstavec 2
Text odstavce: Odstavec 3 

byste se dočkali pouze tohoto:

Text odstavce: Odstavec 1
Text odstavce: Odstavec 2
Text odstavce: 

Je to proto, že objekt $c jsme kontejneru $stranka předali dříve, než nabyl textu 'Odstavec 3'. Bez použití odkazů bychom v kontejneru uchovávali pouze kopie objektů $a, $b, $c, a tedy příkaz $c->text = 'Odstavec 3' by se objektu uloženého v kontejneru vůbec netýkal.

Potřebujete-li vyrobit funkci či metodu, která vrací odkaz na objekt, musí její hlavička vypadat takto:

function &ziskej($parametr) {
  # ...
  return $objekt;
} 

Bez & před názvem funkce bychom příkazem return $objekt vrátili pouze kopii objektu $objekt.

Poznámka: Machrování s odkazy se dá obecně použít na proměnnou jakéhokoliv typu. Podrobněji o odkazech viz manuál k PHP.

Dokončení příště…

Našli jste v článku chybu?