Další novinky v PHP5

27. března 2003

PHP5 nám (snad) nabídne řadu novinek. Naváži na přehled z minulého článku a ponořím se do zpracování výjimek, a tedy mnohem snazší obsluhy chyb, dále zaostřím na jmenné prostory a na pravděpodobné změny při práci s řetězci.

Výjimky

Mechanizmus výjimek je dnes přítomen snad ve všech vyšších programovacích jazycích. Podstatným způsobem ulehčuje práci programátorům a umožňuje obsloužit chybu v místě, kde je to vhodné, což nemusí být vždy místo vzniku. Na místě vzniku je pouze vyvolána výjimka pomocí klíčového slova throw. Tato výjimka musí být zachycena blokem catch v některém nadřazeném bloku.

Obvykle existují dvě varianty obsluhy výjimek. Try-catch a try-finally. V obou případech jsou příkazy, jež mohou způsobit vyvolání výjimky, uzavřeny v bloku try. Pomocí bloku catch je možné reagovat na různé druhy výjimek (případně některé nezachytit vůbec a nechat jejich obsluhu na nadřazeném bloku). Pomocí bloku finally je bez ohledu na typ výjimky možné provést určité operace, jako například uvolnění zdrojů, logování vzniklé situace atd. Obvykle je možné použít obě tyto konstrukce zároveň vnořením dvou bloků do sebe:

try {
  try {
    // kód potenciálně vyvolávající výjimku
  }
  catch (Exception e) {
    // obsluha výjimky
  }
}
finally {
  // uvolnění zdrojů
}

V PHP je situace poněkud odlišná. Výjimkou může být libovolná třída, respektive její instance. Pomocí throw prostě vytvoříte objekt, s nímž PHP nadále zachází jako s výjimkou a umožní jeho zachycení příkazem catch. Blok finally (zatím) k dispozici není. Obsluha výjimek tedy vypadá nějak takto:

class Vyjimka {
  private $zprava;
  function __construct($z = „“) {
    $this->zprava = $z ? $z : „Došlo k výjimce“;
  }
  function Zobraz() {
    echo „{$this->zprava}\n“;
  }
}
try {
  // vyvolání výjimky:
  throw new Vyjimka(„Chyba!“);
}
catch (Vyjimka $v) {
  // obsluha výjimky:
  $v->Zobraz();
}

Jedná se o znatelné zlepšení oproti dosavadnímu stavu, přesto si zvláště kritici PHP neodpustí několik připomínek:

  • Již zmíněná absence bloku finally.
  • Chybí systémová podpora hierarchie výjimek, což nutí uživatele si odpovídající strukturu navrhnout sám. Doufejme, že se vhodná hierarchie výjimek objeví v některé z podpůrných knihoven.
  • V současné implementaci není příliš dobrá podpora ladění aplikací využívajících výjimky. Pokud se vám totiž stane, že vzniklá výjimka nebude obsloužena (vznikne mimo blok try nebo typ výjimky v bloku catch neodpovídá vytvořenému objektu), obdržíte pouze chybové hlášení: Fatal error: Uncaught exception! in Unknown on line 0. Z toho se opravdu těžko poznává, o jakou výjimku šlo a kde vznikla.

Poněkud nepříjemné je také to, že nový model se zatím nedá použít na již existující funkce, neboť jejich činnost se nezměnila. Chápu snahu o zachování zpětné kompatibility v maximální možné míře, ale tento stav vede ke schizofrenii, kdy výjimky bude možné použít jen někde, a stávajícího testování chybových stavů po každém volání funkce se nezbavíme. Jistou berličkou může být například tento postup:

function connectDB($dbname, $username=““, $password=““, $host=“localhost“) {
  if (!mysql_connect($host, $username, $password)) throw new Vyjimka(„Nezdařilo se připojení k DB serveru.“);
  if (!mysql_select_db($dbname)) throw new Vyjimka(„Nezdařilo se připojení k databázi $dbname.“);
}
function queryDB($query) {
  if ($result = mysql_query($query)) return $result;
  else throw new Vyjimka(„Chybný dotaz: $query „);
}

Nepochybuji o tom, že podobný způsob obsluhy bude (alespoň volitelně) zařazen do nových knihoven jako PEAR či PHPlib, a to nejen v jejich databázovém rozhraní. Uvažuje se také o tom, že by existoval režim (podobně jako různé úrovně hlášení chyb, E_NOTICE, E_WARNING apod.), při kterém by běžné funkce generovaly výjimky v případě výskytu chyby.

Jmenné prostory

O jmenných prostorech v PHP se diskutuje již velmi dlouho a během posledního cca půl roku, kdy se o PHP5 začalo mluvit v konkrétnějších obrysech, se objevilo několik různých způsobů, jak tuto problematiku v nové verzi řešit. Jednou měla být tato vlastnost imitována pomocí vnořených tříd, jinde se zase v článku objevily informace o balíčcích (packages). V článcích, jež na toto téma vyšly, se těžko orientuje, když v každém najdeme něco jiného, proto je nejlepším způsobem si vše vyzkoušet. Nyní tedy jmenné prostory fungují následujícím způsobem:

  • Jmenný prostor je ohraničen blokem, jenž začíná klíčovým slovem namespace a názvem jmenného prostoru. Uvnitř bloku mohou být deklarace tříd, proměnných, konstant i funkcí, jež jsou platné pouze v daném jmenném prostoru.
  • Vně jmenného prostoru je možné se na uvnitř obsažené definice odkazovat pomocí prefixu tvořeného názvem jmenného prostoru a operátoru „::“, podobně jako při přístupu ke statickým vlastnostem a metodám.
  • Aby se zamezilo případným konfliktům jmen, není možné, aby ve skriptu existovala globální třída a jmenný prostor se stejným názvem.
  • Často používané deklarace je možné „načíst“ do aktuálního jmenného prostoru pomocí direktivy import.
  • Jmenné prostory nelze do sebe vnořovat.
  • Pro lepší vizuální strukturovanost je možné v názvu jmenného prostoru používat dvojtečky (ne však dvě ihned za sebou), například tedy com:firma:projekt:balicek. Nemá to však žádný vliv na vztahy mezi třídami či funkcemi z různých jmenných prostorů, jež mají podobné názvy.

Volání tříd a funkcí z jmenného prostoru si ukážeme na jednoduchém příkladu:

namespace Pokus {
  class Foo {
    function bar() { … }
  }
  function FooBar() { … }
}
Pokus::Foobar(); // ok
FooBar();        // nefunguje
import function FooBar from Pokus;
FooBar();        // ok
// nebo také takto:
import * from Pokus;
$pF = new Foo();
$pF->bar();

Vícenásobná dědičnost

Nové PHP bude též podporovat vícenásobnou dědičnost, a to v „kontrolovatelnější“ podobě, tj. s využitím rozhraní. Kromě tříd bude možné deklarovat také rozhraní, pomocí klíčového slova interface, a každá třída bude moci dědit od jedné rodičovské třídy a libovolného množství rozhraní:

interface MojeRozhrani {
  function PredstavSe();
}
class NovaTrida extends Braza:Priklady::Zakladni implements MojeRozhrani {
  function PredstavSe() {
    // deklarace funkce
  }
  …
}

Z výše uvedeného příkladu je zřejmé, jak to celé funguje. V rozhraních je možné deklarovat pouze funkce, respektive jejich hlavičku, a každá třída, jež dané rozhraní implementuje, je povinna všechny příslušné metody nadefinovat.

S tím také souvisí abstraktní třídy a metody. Pomocí klíčového slova abstract je možné označit třídu či metodu jako abstraktní. Třída obsahující alespoň jednu abstraktní metodu musí být označena jako abstraktní, stejně jako třída, jež implementuje rozhraní a neimplementuje jeho metody (tj. v podstatě obsahuje pouze abstraktní deklarace těchto metod).

Řetězce

Drobná změna se dotkne také práce s řetězci. Jedná se o příjemné vylepšení, které ovšem může mít dopad na kompatibilitu starých skriptů. V současné době se totiž pro přístup k jednotlivým znakům řetězce používá stejný zápis jako v případě polí, tj. uvedení indexu v hranatých závorkách:

$retezec = „Ahoj lidi!“;
$retezec[5] = „L“;

Problém ale může vzniknout, pokud byl řetězec dosud prázdný. Pak například volání $retezec[0] = "a"; způsobí konverzi proměnné na pole a nastavení jejího prvního prvku na hodnotu a, nikoli vytvoření řetězce. V PHP5 je zavedena nová syntaxe, která má těmto nejednoznačnostem zabránit. Pro zápis indexu znaku v řetězci se používají složené závorky, např. tedy takto:

$retezec{0} = „a“;

Původní syntaxe zatím zůstává v platnosti, ale uvažuje se, že bude označená za zastaralou (deprecated) a takové použití bude generovat chybu, nejspíše typu E_NOTICE (pozor, tato chyba se standardně nezobrazuje). V aktuální implementaci však ke generování hlášení nedochází.

Pokud máte nyní obavy, zda bude nadále možné odkazovat znaky z řetězce uvnitř složitějších výrazů v textu pomocí složených závorek, jistě vás potěší, že tato vlastnost nedoznala změny a funguje i s novým způsobem zápisu:

$retezec = „Ahoj lidi!“;
echo „Text: $retezec, čtvrtý znak: {$retezec{3}}, desátý znak: {$retezec{9}}“;

PHP5

Jak je vidět, PHP5 se novými změnami významným způsobem přiblížilo možnostem mnohých moderních programovacích jazyků. Samozřejmě, že nelze úplně srovnávat, jak vyplývá i z vašich příspěvků do diskuze okolo předchozího článku. Každý z těchto jazyků je zaměřen primárně na něco jiného, má své výhody a nevýhody. Zatímco PHP je primárně zaměřeno na webové aplikace a jeho integrace s WWW stránkami je vynikající, stejně jako podpora databází, jiné jazyky mají třeba výhodu v kompilaci, komplexním modelu tříd a podobně. Ale právě tam, kde se oblasti využití dotýkají, může PHP získávat nové uživatele – a nové motivace pro vývoj. Právě rozsáhlé knihovny jako PEAR či PHP-GTK narážely na hranice možností PHP a i díky nim máme dnes novou verzi PHP s lepšími objekty, násobnou dědičností a výjimkami.

Odkazy a zdroje

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

Štítky: Články

Mohlo by vás také zajímat

Nejnovější

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *