A PHP programozási nyelv

Kivételkezelés

Kivételek

Élet a kivételek előtt

A PHP 4-ben már bevezetésre került ugyan a kivételkezelés, azonban PHP 5 előtt inkább jellemzően csak kezdetleges hibakezeléssel kellett beérni a programozóknak. Ha egy alprogramban hiba lépett fel, akkor a programozó választhatott, hogy die() segítségével befejezi a programot, vagy a függvénye -1 vagy valamilyen hamis eredménnyel térjen vissza. A die([ string $status ] ) függvény meghívásával teljesen leáll az értelmezés, így ha az adott programszakasznak az volt a célja hogy valamilyen weboldalt állítson elő, akkor szinte biztosak lehetünk, hogy a végeredményként kapott HTML kimenet invalid.

Példa a die() függvény használatára:
$filename = '/path/to/data-file'; $file = fopen($filename, 'r') or die("unable to open file ($filename)");

A die() függvényt gyakran használják az or operátor segítségével, annak lusta módon történő kiértékelése végett.

Kivétel (Exception)

A PHP 5 kényelmes módot tesz lehetővé a kivételkezelés használatára. A kivételkezelés modellje hasonló más programozási nyelvekéhez. Ez a megvalósítás lehetővé teszi, hogy a tagfüggvények áthárítsák a hibakezelés terhét az ügyfélkódra, amikor valamilyen hiba lép fel.

A kivétel egy olyan objektum amelyet a kiváltáskor automatikusan feltölt a hibára jellemző értékekkel, beleértve a hiba helyét is. Minden kivétel – hasonlóan a többi kivételkezelő nyelvhez – egy közös ősosztályból, az Exception osztályból származik. Az Exception példányokat a programozónak kell létrehozni a hiba kiváltásakor a new kulcsszóval. Ezután a throw utasítással vissza tudjuk dobni az ügyfélkódnak. Ilyenkor a tagfüggvény végrehajtása teljesen befejeződik.

function inverse($x) { if (!$x) throw new Exception('Division by zero.'); else return 1/$x; }

Az Exception osztály konstruktorának adhatunk a hibaüzenetre vonatkozó szöveges üzenetet, vagy tetszőleges hibakódot is.

Ha a tagfüggvény dobhat kivételt, akkor biztosítanunk kell, hogy hívóoldalon azt a kivételt el is fogjuk. Ehhez szükség van a try és a catch utasításpárokra. A try utasítás segítségével megpróbálhatunk végrehajtani egy adott blokkot, amiben ha kivétel váltódik ki, akkor azt a catch blokkban lehetőségünk van lekezelni.

try {} catch () {} [catch () {} ]

Lehetőség van további catch ágak megadására, mint ahogy a fenti példa mutatja. Ilyenkor ne felejtsük el hogy az általánosabb kivételeket mindig érdemes hátrébb elhelyezni, mert különben a speciálisabb kivételeket esélye sem lesz a PHP-nek lekezelni, mert egyszerűen soha nem kerülhetnek sorra.

try { echo inverse(5) . "\n"; echo inverse(0) . "\n"; } catch (Exception $e) { echo 'Caught exception: ', $e->getMessage(), "\n"; }

Ha kivételt keletkezik, a blokkban a következő kód már nem fut le, és a PHP megkeresi az első catch blokkot, amire illeszkedik a kivétel. Ha a kivételt nem sikerült elkapni, a PHP fatális hibát (Fatal Error) ad ki "Uncaught Exception ..." szöveggel, hacsak nem rendelkeztünk máshogy a set_exception_handler() beállításával.

Kivételek kiterjesztése

A beépített Exception osztályt lehetőségünk tovább fejleszteni, ha elkészítjük egy alosztályát és kibővítjük saját funkcionalitással.

A beépített Exception osztály szerkezete nagyjából a következő:

class Exception { /* Properties */ protected string $message ; protected int $code ; protected string $file ; // kivétel fellépésének file neve protected int $line ; // a fellépés sora /* Methods */ public __construct ([string $message="" [, int $code=0 [,Exception $previous=NULL ]]] ) final public string getMessage () final public Exception getPrevious () // ha volt megelőző kivétel final public mixed getCode () final public string getFile () final public int getLine () final public array getTrace () // hivási lánc final public string getTraceAsString () // hivási lánc string formában public string __toString () final private void __clone () }

Látható hogy csak a konstruktor illetve a __toString() mágikus metódus felüldefiniálható. Amennyiben felüldefiniálnánk a konstruktort, ajánlott a biztonság kedvéért meghívni a szülő osztály konstruktorát is.

Amikor lekezelünk egy kivételt, dönthetünk úgy, hogy továbbadjuk a kivétel lekezelésének felelősségét és kiváltunk egy újabb kivételt. Ilyenkor a kiváltott kivételben lehetőségünk van eltárolni az előző kivételt. Ily módon az egyes kivételeket láncszerűen egymás után tudjuk fűzni. Amikor a legkülső helyen be szeretnénk járni ezt a létrehozott láncot, akkor például a következő módon járhatunk el:

} catch(Exception $e) { do { /* some inner activity */ } while($e = $e->getPrevious()); }

Saját kivétel definiálása:

class LowBatteryException extends Exception { public function __toString() { return "exception '".__CLASS__ ."' with message '". $this->getMessage()."' in ". $this->getFile().":". $this->getLine()."\nStack trace:\n". $this->getTraceAsString(); } }

Majd ennek a kivételnek a kiváltása:

class LightSaber { private $color; private $acumlator = 100; function __construct($color) { $this->color=$color; } function turnOn(){ if ($this->acumlator == 0) throw new LowBatteryException('Opps'); } } class Jedi { private $lightSaber; private $name; function __construct($name, $color) { $this->name = $name; $this->lightSaber = new LightSaber($color); } function duel() { $this->lightSaber->turnOn(); /* ... */ } }

A kivétel lekezelése:

try { $luke = new Jedi('Luke Skywalker', 'Green'); $luke->duel(); } catch (Exception $e) { echo $e; }

A futás eredménye:

exception 'LowBatteryException' with message 'Opps' in E:\php\jedi.php:17 Stack trace: #0 E:\php\jedi.php(29): LightSaber->turnOn() #1 E:\php\jedi.php(37): Jedi->duel() #2 {main}