PHP - Rukovanje izuzecima (Exception Handling)


Šta je izuzetak?

Izuzetak je signal koji ukazuje na to da se dogodila neka vrsta izuzetnog događaja ili greške. Izuzeci mogu nastati iz različitih razloga, na primjer, nije usppijela veza sa bazom podataka ili neuspjeh upita, pokušavanje pristupiti datotekama koje ne postoje itd.PHP pruža moćan mehanizam za rukovanje izuzecima koji vam omogućuje graciozno rukovanje izuzecima. Za razliku od tradicionalnog PHP-ovog sistema za rukovanje greškama, rukovanje izuzecima je objektno orijentirana metoda za rukovanje greškama, koja pruža kontrolisaniji i fleksibilniji oblik izvještavanja o greškama. Model izuzetka prvi je put predstavljen u PHP 5.



Korištenje Throw i Try...Catch izjava

U pristupu zasnovanom na izuzecima, programski kod se zapisuje u try blok, izuzetak se može izbaciti (throw) pomoću naredbe throw kada se za vrijeme izvršavanja koda u bloku (try) pokušaja dogodi izuzetan događaj. Zatim se hvata (catch) i razrješava jedan ili više blokova ulova. Sljedeći primjer pokazuje kako funkcioniše rukovanje izuzecima:

<?php
function division($dividend, $divisor){
    // Izbaci (Throw) izuzetak ako je djelilac nula
    if($divisor == 0){
        throw new Exception('Podjela s nulom.');
    } else{
        $quotient = $dividend / $divisor;
        echo "<p>$dividend / $divisor = $quotient</p>";
    }
}
 
try{
    division(10, 2);
    division(30, -4);
    division(15, 0);
    
    // Ako se izuzetak izbaci (throw) , sljedeći red se neće izvršiti
    echo '<p>Sve podjele su uspješno izvršile.</p>';
} catch(Exception $e){
    // Obradite izuzetak
    echo "<p>Uhvaćen izuzetak: " . $e->getMessage() . "</p>";
}
 
// Nastavite izvršenje
echo "<p>IT TUTORIJALI!</p>";
?>

Možda se pitate o čemu se radi u ovom kodu? Pa, prođimo svaki dio ovoga koda jedan po jedan red radi boljeg razumijevanja.


Objašnjenje koda

PHP-ov sistem za rukovanje izuzecima sastoji se u osnovi od četiri dijela: try, throw, catch i Exception klase. Sljedeća lista opisuje kako svaki dio tačno radi.

  • Funkcija division() u gornjem primjeru provjerava da li je djelitelj jednak nuli. Ako jeste, izuzetak se izbacuje preko PHP-ove naredbe throw. Inače ova funkcija vrši dijeljenje pomoću zadatih brojeva i prikazuje rezultat.

  • Kasnije se funkcija division() poziva unutar bloka try s različitim argumentima. Ako se generiše izuzetak tokom izvršavanja koda unutar bloka try, PHP zaustavlja izvršavanje u tom trenutku i pokušava pronaći odgovarajući blok catch. Ako se pronađe, izvršava se kod unutar tog bloka catch, ako ne, generiše se fatalna greška.
  • Blok catch obično uhvati izuzetak throw unutar bloka try i kreira objekt ($e) koji sadrži informacije o izuzetku. Poruka greške iz ovog objekta može se dobiti pomoću metode getMessage() izuzetaka.

Klasa izuzetaka PHP-a također nudi metode getCode(), getFile(), getLine() i getTraceAsString() koje se mogu koristiti za generisanje detaljnih informacija o otklanjanju grešaka.

<?php
// Isključite zadano izvještavanje o greškama
error_reporting(0);
 
try{
    $file = "somefile.txt";
    
    // Pokušaj otvaranja datoteke
    $handle = fopen($file, "r");
    if(!$handle){
        throw new Exception("Nije moguće otvoriti datoteku!", 5);
    }
    
    // Pokušaj čitanja sadržaja datoteke
    $content = fread($handle, filesize($file));
    if(!$content){
        throw new Exception("Nije moguće pročitati datoteku!", 10);
    }
    
    // Zatvaranje rukovanja datoteke
    fclose($handle);
    
    // Prikaz sadržaja datoteke
    echo $content;
} catch(Exception $e){
    echo "<h3>Hvatanje izuzetka!</h3>";
    echo "<p>Poruka o grešci: " . $e->getMessage() . "</p>";    
    echo "<p>Datoteka: " . $e->getFile() . "</p>";
    echo "<p>Linija: " . $e->getLine() . "</p>";
    echo "<p>Greška koda: " . $e->getCode() . "</p>";
    echo "<p>Prati: " . $e->getTraceAsString() . "</p>";
}
?>

Konstruktor izuzetaka po želji uzima poruku o izuzetku i kod izuzetka. Iako se poruka o izuzetku obično koristi za prikaz generičkih informacija o tome što je pošlo po zlu, kod izuzetka može se koristiti za kategorizaciju grešaka. Navedeni kod izuzetka može se kasnije dohvatiti metodom getCode() izuzetka.



Definisanje prilagođenih izuzetaka

Možete čak definisati vlastite prilagođene rukovatelje izuzetaka da biste različite vrste izuzetaka tretirali na drugačiji način. Omogućava vam upotrebu zasebnog bloka ulova za svaku vrstu izuzetka. Možete definisati prilagođeni izuzetak proširivanjem klase Exception, jer je Exception osnovna klasa za sve izuzetke. Klasa prilagođenog izuzetka nasljeđuje sva svojstva i metode iz PHP-ove klase izuzetke. Također možete dodati svoje prilagođene metode u klasu prilagođenih izuzetaka. Pogledajmo sljedeći primjer:

<?php
// Proširenje klase Exception
class EmptyEmailException extends Exception {}
class InvalidEmailException extends Exception {}
 
$email = "milosmihaljica@example..com";
 
try{
    // Izbaci izuzetak ako je e-pošta prazna
    if($email == ""){
        throw new EmptyEmailException("<p>Unesite svoju e-mail adresu!</p>");
    }
    
    // Izbaci izuzetak ako e-pošta nije važeća
    if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {           
        throw new InvalidEmailException("<p><b>$email</b> nije važeća adresa e-pošte!</p>");
    }
    
    // Prikaži poruku o uspjehu ako je adresa e-pošte važeća
    echo "<p>SUCCESS: Provjera valjanosti e-pošte uspješna.</p>";
} catch(EmptyEmailException $e){
    echo $e->getMessage();
} catch(InvalidEmailException $e){
    echo $e->getMessage();
}
?>

U primjeru smo izveli dvije nove klase izuzetaka: EmptyEmailException i InvalidEmailException iz osnovne klase Exception. Višestruki blokovi catch koriste se za prikaz različitih poruka o greškama, zavisno o vrsti generisanog izuzetka. Budući da ove prilagođene klase izuzetaka nasljeđuju svojstva i metode iz klase Exception možemo se koristiti metode klase Exception poput getMessage(), getLine(), getFile() itd. za dohvatanje informacija o greškama iz objekta izuzetaka.



Postavljanje rukovaoca globalnim izuzecima

Kao što smo ranije govorili u ovoj lekciji, ako se izuzetak ne uhvati, PHP generiše fatalnu grešku s porukom "Uncaught Exception ...". Ova poruka o grešci može da sadrži osjetljive informacije poput imena datoteke i broja reda gdje se problem javlja. Ako ne želite izložiti takve informacije korisniku, možete stvoriti prilagođenu funkciju i registrovati je s funkcijom set_exception_handler() za rukovanje svim neuhvaćenim izuzecima.

<?php
function handleUncaughtException($e){
    // Prikazuje korisniku generičku poruku o grešci
    echo "Opps! Nešto je pošlo po zlu. Pokušajte ponovo ili nas kontaktirajte ako se problem nastavi.";
    
    // Konstruiše error string
    $error = "Uncaught Exception: " . $message = date("Y-m-d H:i:s - ");
    $error .= $e->getMessage() . " in file " . $e->getFile() . " on line " . $e->getLine() . "\n";
    
    // Zabilježava detalje greške u datoteci
    error_log($error, 3, "var/log/exceptionLog.log");
}
 
// Registrujte prilagođeni rukovatelj izuzecima
set_exception_handler("handleUncaughtException");
 
// Izbaci (throw) izuzetak
throw new Exception("Izuzetak za testiranje!");
?>