Hlavní navigace

Zaostřeno na PHP (3)

Michal Burda

Toto je poslední (?) díl věnovaný tomu, co nám nové jádro Zend 2 jazyka PHP přinese -- pod drobnohledem se dnes ocitnou konstanty, vnořené třídy (názvové prostory) a výjimky.

Konstanty

Uvnitř tříd je nyní možno pomocí klíčového slova const definovat konstanty:

<?
class Trida {
  const X = 30;

}

echo Trida::X;
?>

Globální konstanty se ale definují pořád stejně:

<?

define('Y', 100);
echo Y;
?>

Vnořené třídy

Mechanismus vnořených tříd je pokusem tvůrců jazyka vnést do PHP něco jako názvové prostory. Problém starého jádra (Zend 1) spočíval v tom, že existovaly jen tři obory platnosti identifikátorů: globální, třídní a obor platnosti identifikátorů uvnitř funkce. Docela jednoduše pak mohla nastat kolize, kdy různé knihovny si nazvaly různé funkce (ale i třídy, konstanty či globální proměnné) stejnými jmény.

Nový Zend 2 přichází s konceptem vnořených tříd, kde každá třída vnitřně představuje zvláštní tabulku symbolů. Uvnitř třídy můžete nyní definovat statické proměnné a funkce, ale i konstanty či další třídy. Může tak vzniknout celá hierarchie názvových prostorů, aniž by identifikátory jednotlivých prvků spolu nějak kolidovaly.

<?
/* Třídy mohou obsahovat třídy */

class Tvary::Kvadr {
  function kresli() {
    echo "kvadr<br>";
  }

}

class Tvary::Koule {
  function kresli() {

    echo "koule<br>";
  }
}

$x = new Tvary::Kvadr();

$x->kresli();
$x = new Tvary::Koule();

$x->kresli();
?>

Pro přístup k lokálním prvkům třídy se zavedl předřaďovač self::. Můžete také používat staré přístupové konvence, kdy místo self:: použijete jméno třídy (tedy např. self::$static­ka_promenna ne­bo

MojeTrida::$sta­ticka_promenna).

Příklad:

<?
/* Práce se statickou proměnnou */

class Trida {
  static $promenna = 0;

  function delej() {

    self::$promenna++;
    Trida::$promenna++;
  }
}

Trida::delej(); // statické volání
echo Trida::$promenna; // vypíše "2"

?>

Pro konstanty a funkce navíc platí, že když je použijete přímo bez uvedení kontextu, bude PHP prohledávat nejprve aktuální třídu a teprve potom (v případě neúspěchu) se podívá do vnější třídy nebo globálního prostoru. Chcete-li uvnitř třídy volat přímo nějakou „globální“ funkci a chcete-li mít jistotu, že se místo ní nezavolá nějaká vnitrotřídní funkce stejného jména, musíte použít předřaďovač main::. Tak například volání main::strlen() vám zaručí, že budete volat funkci strlen() z hlavního oboru platnosti identifikátorů. Ovšem o toto všechno se musíte starat jen tehdy, pokud budete své lokální funkce pojmenovávat stejně jako globální…

Příklad:

<?
/* Lokální funkce mají přednost */

function delej_neco_jineho() {
  echo 'nic...<br>';

}

class Trida {
  function delej() {
    $this->delej_neco_jineho();
    delej_neco_jineho();
  }

  function delej_neco_jineho() {
    echo 'něco...<br>';
  }

}

$x = new Trida();
$x->delej();

?>

…výstupem bude:

něco...
něco...

Další příklad:

<?

/* Práce s konstantami */

define('KONSTANTA', '20');

class Trida {

  const KONSTANTA = 3.14;

  function delej($k) {

    echo "$k<br>";
    echo KONSTANTA . '<br>';
    echo self::KONSTANTA . '<br>';
    echo Trida::KONSTANTA . '<br>';
  }

}

$x = new Trida();
$x->delej(Trida::KONSTANTA);

?>

…výsledkem bude čtyřikrát po sobě hodnota Ludolfova čísla zaokrouhlená na dvě desetinná místa.

Krátká poznámka o statických metodách

V PHP se v definici třídy stále (alespoň jsem o opaku nikde neslyšel) nebudou rozlišovat statické (třídní) a obyčejné metody. Zřejmě nadále bude platit, že v podstatě každou metodu budete moci zavolat jak staticky (Trida::metoda()), tak dynamicky ($objekt->metoda()). Jediný rozdíl mezi oběma voláními je v tom, že při dynamickém volání metody je uvnitř funkce přístupná proměnná $this odkazující na aktuální objekt:

<?
class Trida {
  function delej() {

    if (isset($this)) {
      echo 'dynamické volání<br>';
    } else {

      echo 'statické volání<br>';
    }
  }
}

$x = new Trida();

$x->delej();
Trida::delej();
?>

Jako výsledek můžete čekat:

dynamické volání
statické volání

Výjimky

Některý z tvůrců jazyka PHP se dal slyšet, že „Java programmers would love PHP,“, tj. že programátoři v Javě budou mít PHP rádi. Přestože plamenné debaty provázející tento seriál tomu zatím moc nenasvědčují, nelze přehlédnout, že právě tomuto jazyku se PHP začíná po syntaktické stránce (třebaže přes značné porodní bolesti) nějak nápadně podobat. (Nebo je to radši C++?) Důkazem budiž třeba další jeho nová vlastnost – zpracování výjimek.

Princip výjimek se v jiných jazycích velmi dobře osvědčil. V podstatě jde o umožnění programátorovi předčasně opustit určitou část kódu, pokud se při jeho zpracovávání vyskytne chyba.

Bez mechanismu výjimek se obvykle programuje tak, že každá funkce, která může skončit neúspěchem, vrací nějaký chybový kód, nebo potvrzení, že vše proběhlo v pořádku. Úkolem programátora pak je výsledek každé funkce ručně testovat a na případnou chybu příslušně reagovat. Takové programování pak je velice nudné, těžkopádné, stereotypní, zbytečně prodlužuje zdrojový kód a vůbec – často se na kontroly chybových stavů zapomíná (nebo vykašle) úplně.

Naproti tomu programování s výjimkami předpokládá, že vše probíhá úspěšně. Pouze pro případ chyby (výjimky) se na konec kritického úseku napíše obslužný kód, který na případná selhání nějak reaguje. V PHP podobně jako v Javě se kritický kód označuje klíčovým slovem try, obslužná rutina příkazem catch a výjimka se generuje konstrukcí throw. Vše si předvedeme na příkladu:

<?
/* definice třídy výjimky */
class MojeVyjimka {

  var $hlaseni;

  function __construct($hlaseni) {
    $this->hlaseni = $hlaseni;
  }

  function vypis() {
    echo '<p>Chyba! ' . $this->hlaseni;
  }

}


/* definice funkce, která v případě chyby generuje výjimku */
function deleni($x, $y) {

  if ($y == 0) {
    throw new MojeVyjimka('Dělení nulou!');
  }

  return $x / $y;
}


try {

  // kritický úsek - chceme zachytávat výjimky
  $a = deleni(10, 0);
  echo "Výsledek: $a";

} catch (MojeVyjimka $e) {
  // tento kód se provede jen v případě výskytu výjimky MojeVyjimka
  $e->vypis();

}
?>

V příkladu jsme si nadefinovali svou třídu pro výjimku a obohatili jsme ji základní schopností přenášet nějakou informaci o chybě. Dále jsme vytvořili ukázkovou funkci deleni(), která provádí obyčejnou operaci dělení; navíc však testuje hodnotu druhého operandu a při pokusu o dělení nulou generuje výjimku. Poté už jen následuje kód, který funkci deleni() používá. Blok je uzavřen v příkazu try, který indikuje, že v něm může dojít k výjimce. Co se má dělat v případě výskytu výjimky, říká příkaz catch: pokud se vyskytne výjimka MojeVyjimka, uloží se do proměnné $e a zpracuje se blok příkazů za tímto klíčovým slovem.

Výsledkem ukázky bude pouze chybové hlášení: Chyba! Dělení nulou!

Prosím, všimněte si, že v případě výskytu výjimky se ve zpracovávání kritického úseku nepokračuje, ale ihned se skočí na obslužný kód výjimky, takže text o výsledku operace následující za chybným voláním funkce deleni() se nikdy nevypíše.

Úseků catch může být za sebou hned několik, s tím, že každý obsluhuje jiný druh výjimky. Také, podobně jako v jiných jazycích, i v PHP může být díky dědičnosti definována hierarchie výjimek. Pak platí, že se vždy provede ten blok za příkazem catch, který je označen jménem stejné třídy nebo nadtřídy k třídě generované výjimky.

Pochopitelně, mechanismus výjimek je k ničemu (nebo téměř k ničemu), pokud není hojně podporován samotným jazykem a zejména jeho knihovními funkcemi. Doufejme, že si toho jsou vědomi i tvůrci Zendu 2 a že se dočkáme i toho, že všechny funkce jazyka PHP na tento systém hlášení chybových stavů postupně přejdou.

A to by bylo vše, co se mi o připravovaném PHP podařilo zjistit. Pokud víte ještě o něčem jiném, nezapomeňte se o své znalosti podělit v diskusi. :-)

Našli jste v článku chybu?

21. 6. 2010 22:16

obi (neregistrovaný)

Tak jsem na PHP 5.3.1 zkoušel ten příklad s vnořenými třídami a háže to chybu:
Parse error: syntax error, unexpected T_PAAMAYIM_NE­KUDOTAYIM, expecting ‚{‘
Prosím co dělám špatně?



2. 10. 2002 20:50

petr (neregistrovaný)

Mě se PHP moc líbí. Přešel jsem na něj z Pascalu a je to rozhodně lepší. Úplně nejlepší je možnost pracovat MySQL.

Podnikatel.cz: Udávání a účtenková loterie, hloupá komedie

Udávání a účtenková loterie, hloupá komedie

Měšec.cz: U levneELEKTRO.cz už reklamaci nevyřídíte

U levneELEKTRO.cz už reklamaci nevyřídíte

Vitalia.cz: Paštiky plné masa ho zatím neuživí

Paštiky plné masa ho zatím neuživí

DigiZone.cz: Recenze Westworld: zavraždit a...

Recenze Westworld: zavraždit a...

Podnikatel.cz: Změny v cestovních náhradách 2017

Změny v cestovních náhradách 2017

Vitalia.cz: Jsou čajové sáčky toxické?

Jsou čajové sáčky toxické?

Měšec.cz: Jak levně odeslat balík přímo z domu?

Jak levně odeslat balík přímo z domu?

120na80.cz: Na ucho teplý, nebo studený obklad?

Na ucho teplý, nebo studený obklad?

Měšec.cz: Jak vymáhat výživné zadarmo?

Jak vymáhat výživné zadarmo?

DigiZone.cz: Rádio Šlágr má licenci pro digi vysílání

Rádio Šlágr má licenci pro digi vysílání

Podnikatel.cz: Udávání kvůli EET začalo

Udávání kvůli EET začalo

Vitalia.cz: Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Láska na vozíku: Přitažliví jsme pro tzv. pečovatelky

Podnikatel.cz: Snížení DPH na 15 % se netýká všech

Snížení DPH na 15 % se netýká všech

Lupa.cz: Propustili je z Avastu, už po nich sahá ESET

Propustili je z Avastu, už po nich sahá ESET

Vitalia.cz: Pamlsková vyhláška bude platit jen na základkách

Pamlsková vyhláška bude platit jen na základkách

Podnikatel.cz: Chtějte údaje k dani z nemovitostí do mailu

Chtějte údaje k dani z nemovitostí do mailu

Podnikatel.cz: Podnikatelům dorazí varování od BSA

Podnikatelům dorazí varování od BSA

120na80.cz: Horní cesty dýchací. Zkuste fytofarmaka

Horní cesty dýchací. Zkuste fytofarmaka

Lupa.cz: Co se dá měřit přes Internet věcí

Co se dá měřit přes Internet věcí

Root.cz: Certifikáty zadarmo jsou horší než za peníze?

Certifikáty zadarmo jsou horší než za peníze?