Hlavní navigace

Zaostřeno na PHP (po roce) 2

Michal Burda

Pokračuje exkurze po vlastnostech a změnách syntaxe připravovaného PHP 5.

Typová kontrola

Za revoluční počin považuji zavedení nepovinné kontroly typů objektů předávaných jako parametry metodám nebo funkcím. Vše funguje takto: před jméno argumentu v deklaraci metody či funkce lze uvést název třídy (tj. typ argumentu) a PHP potom při každém volání dané metody nebo funkce kontroluje, jestli typy předávaných parametrů odpovídají deklaraci.

<?php
function nejakaFunkce(Trida $t) {
  ...
}

...

$obj1 = new Trida();
$obj2 = new DalsiTrida();
nejakaFunkce($obj1); // V pořádku
nejakaFunkce($obj2); // Vyvolá chybu v případě, že DalsiTrida
                     // není potomkem třídy Trida
?>

Uvádění typu předávaného objektu je nepovinné – nic se tedy nestane, pokud tento mechanismus nebudete využívat. Okradete se pouze o výbornou pomůcku ladění.

Kontrolovat lze tímto způsobem pouze objektové parametry. Nelze tedy psát např. function f(string $s) a podobně. Testy typů se provádějí v čase běhu skriptu. Špatný typ parametru se tedy odhalí, až když na příslušný řádek s chybným kódem při vykonávání PHP narazí. Nelze tedy spoléhat jako u jiných jazyků na to, že jedno spuštění (zkompilování…) odhalí všechny nesrovnalosti s typy.

Operátor instanceof

Tvůrci PHP se nikdy netajili tím, že PHP 5 bude vypadat jako Java. Koneckonců, není nutné neustále vymýšlet objevené a stejné věci pojmenovávat různě. Důkazem mohutné inspirace Javou budiž i nový operátor instanceof. Můžete pomocí něj jednoduše(ji) zjišťovat, jestli je daný objekt instancí třídy, rodiče třídy, nebo implementací rozhraní.

<?php
class JakasiTrida {
  ...
}

$cosi = new JakasiTrida();
...
if ($cosi instanceof JakasiTrida) {
  echo 'nazdar chlapi (a holky)';
}
?>

Ano, čtete dobře, v poslední větě jsem skutečně psal rozhraní – to je další lahůdka OOP zavlečená z Javy. Nikam neodcházejte a čtěte dále…

Rozhraní (interface)

Používání rozhraní (interface) je jedním ze způsobů, jak objektově modelovat náš složitý svět. Při objektově orientovaném programování se občas dostaneme do situace, kdy nějaká třída vykazuje takové vlastnosti, že by bylo vhodné, aby jejím předkem byly současně dvě různé třídy. Přidržím-li se příkladu s ovládacími prvky z minulého dílu a vezmu-li v úvahu hypotetickou třídu Form (formulář), pak tato třída by mohla být potomkem třídyOvladaciPrvek. Pokud bychom ale chtěli třídu Form udělat velice chytře, mohli bychom požadovat, aby automaticky zobrazila a nabídla k editaci obsah nějakého záznamu v databázi. Přístup do databáze je umožněn řekněme pouze potomkům třídy NacitaniDat a my máme problém: Form logicky patří do podstromu ovládacích prvků, ale zároveň je třeba, aby zvládal i načítání dat. Co s tím?

V C++ se podobné situace daly řešit vícenásobnou dědičností, ale kolize názvů položek rodičovských tříd přinášela víc problémů než užitku. V Javě (a teď už i v PHP) se s tím poprali jinak: zavedli rozhraní.

Pojem rozhraní souvisí s rolemi objektu, jaké hraje vůči okolnímu světu. Náš objekt typu Form z předminulého odstavce vystupuje jednou v roli ovládacího prvku (tehdy, když po něm řekněme budeme chtít, aby se „zobrazil“ na stránce) a jindy zase v roli čtečky dat (když bude třeba, aby si data „natáhl“ sám z databáze).

Zamyslíme-li se nad skutečným světem, i zde najdeme řadu objektů, které vystupují v různých rolích: vy sami jako objekty se nyní nacházíte v roli čtenáře článku na Rootu. Až dočtete, přepnete se možná do role návštěvníka kina, člověka, který jde uhasit žízeň do blízké restaurace nebo hospodyně prahnoucí po umývání schodů.

Interface je jazyková konstrukce určená k deklaraci takové role. Neslouží k definici žádného kódu. Pouze vyjmenovává, jaké akce můžeme od objektu disponujícího danou rolí očekávat. Příklad:

<?php
interface KonzumentPiva {
  public function kupPivo();
  public function konzumuj(Pivo $p);
  public function jdiNaWC();
}
?>

Máme-li definováno rozhraní, potřebujeme také nějakou třídu, která by dané rozhraní implementovala:

<?php
class Student implements KonzumentPiva {
  public function kupPivo() {
    ...
  }

  public function konzumuj(Pivo $p) {
    ...
  }

  public function jdiNaWC() {
    ...
  }
}
?>

Vtip je v tom, že zatímco může být třída zděděna pouze z jednoho předka, implementovat může takřka libovolný počet rozhraní (ta se potom uvádějí čárkami oddělená za klíčovým slovem implements v deklaraci třídy).

Jak rozhraní řeší náš problém s třídou Form? Jednoduše. Podle povahy problému se třeba rozhodneme, že Form budeme dědit z abstraktní třídyOvladaciPrvek a navíc bude implementovat rozhraní NacitaniDat, které reprezentuje roli objektu načítajícího data z databáze.

Zatímco například v Javě jsou rozhraní přísně typově kontrolována, v případě PHP jde spíše o čistotu programování, která by nás měla vést k jejich používání. V PHP jakožto slabě typovém jazyce bychom se klidně bez rozhraní obešli. V malých prográmcích je to určitě pochopitelné, ale v rozsáhlejších projektech s desítkami tříd už trochu pořádku vneseného rozhraními neuškodí.

Dokončení příště.

Našli jste v článku chybu?