PHP - Rukovanje greškama (Error Handling)


Rukovanje greškama

Ponekad se vaša aplikacija neće pokrenuti onako kako bi trebala, što rezultuje greškom. Brojni su razlozi koji mogu uzrokovati greške, na primjer:

  • Web serveru možda ponestaje prostora na disku
  • Korisnik je možda uneo neispravnu vrijednost u polje obrasca
  • Datoteka ili zapis baze podataka kojem ste pokušavali pristupiti možda ne postoji
  • Aplikacija možda nema dozvolu za pisanje u datoteku na disku
  • Usluga kojoj aplikacija treba pristupiti možda je privremeno nedostupna

Ove vrste grešaka poznate su kao runtime greške, jer se javljaju u trenutku kada se skripta pokreće. Oni se razlikuju od sintaksnih grešaka koje treba ispraviti prije pokretanja skripte. Profesionalna aplikacija mora imati mogućnosti da graciozno riješi takve greške pri izvođenju. To obično znači informisanje korisnika o problemu jasnije i preciznije.



Razumijevanje nivoa grešaka

Obično, kada postoji problem koji sprječava pravilno izvršavanje skripte, PHP mehanizam pokreće grešku. Svaka greška predstavljena je cjelobrojnom vrijednošću i pridruženom konstantom. Sljedeća tabela navodi sve nivoe grešaka u PHP-u:

Nivo greške Vrijednost Opis
E_ERROR 1 Fatalna greška u vremenu izvođenja koja se ne može oporaviti. Izvršenje skripte odmah se zaustavlja.
E_WARNING 2 Upozorenje o vremenu izvođenja. Nije fatalno i većina grešaka spada u ovu kategoriju. Izvršenje skripte nije zaustavljeno.
E_PARSE 4 Greška raščlanjivanja vremena kompajliranja. Greške raščlanjivanja treba generisati samo raščlanjivač.
E_NOTICE 8 Obavijest o vremenu izvođenja. Označava da je skripta naišla na nešto što bi moglo dovesti do greške, iako bi se situacija mogla dogoditi i kada se skripta normalno izvodi.
E_CORE_ERROR 16 Fatalna greška koja se dogodi tokom početnog pokretanja PHP engine. Ovo je poput E_ERROR-a, osim što ga generišu jezgra PHP-a.
E_CORE_WARNING 32 Nefatalna greška koja se javlja tokom početnog pokretanja PHP engine. Ovo je poput E_WARNING, osim što ga generišu jezgra PHP-a.
E_COMPILE_ERROR 64 Fatalna greška koja se dogodila dok se skripta kompajlirala. Ovo je poput E_ERROR, osim što ga generiše Zend Scripting Engine.
E_COMPILE_WARNING 128 Došlo je do greške koja nije fatalna za vrijeme sastavljanja skripte. Ovo je poput E_WARNING, osim što ga generiše Zend Scripting Engine.
E_USER_ERROR 256 Fatalna poruka koju je generisao korisnik. Ovo je poput E_ERROR, osim što ga generiše PHP skripta koristeći funkciju trigger_error(), a ne PHP engine.
E_USER_WARNING 512 Poruka upozorenja koju generiše korisnik. Ovo je poput E_WARNING, osim što ga generiše PHP skripta koristeći funkciju trigger_error(), a ne PHP. engine
E_USER_NOTICE 1024 Korisnička poruka obavijesti. Ovo je poput E_NOTICE, osim što ga generiše PHP skripta koristeći funkciju trigger_error(), a ne PHP engine.
E_STRICT 2048 Nije striktno greška, već se pokreće kad god PHP naiđe na kod koji bi mogao dovesti do problema ili proslijediti nekompatibilnosti.
E_RECOVERABLE_ERROR 4096 Uhvatljiva fatalna greška. Iako je greška bila fatalna, PHP engin nije je ostavio u nestabilnom stanju. Ako grešku ne uhvati korisnički definisani rukovatelj greškama, aplikacija se prekida jer je to bila E_ERROR greška.
E_ALL 8191 Sve greške i upozorenja, osim E_STRICT prije PHP 5.4.0.
E_DEPRECATED 8192 Obavještenje o vremenu izvođenja koja ukazuje da kod neće raditi u budućim verzijama PHP-a
E_USER_DEPRECATED 16384 Korisnička poruka upozorenja. Ovo je poput E_DEPRECATED, osim što ga generiše PHP kod koristeći funkciju trigger_error(), a ne PHP engine.
E_ALL 32767 Sve greške i upozorenja, osim nivoa E_STRICT prije PHP 5.4.0.

PHP mehanizam pokreće grešku kad god naiđe na problem s vašom skriptom, ali greške možete i sami pokrenuti kako biste generisali više korisničkih poruka o greškama. Na ovaj način možete svoju prijavu učiniti sofisticiranijom. Sljedeći dio opisuje neke od uobičajenih metoda koje se koriste za rukovanje greškama u PHP-u:



Osnovno rukovanje greškama pomoću funkcije die()

Pogledajmo sljedeći primjer koji jednostavno pokušava otvoriti tekstualnu datoteku samo za čitanje.

<?php
// Pokušajte otvoriti nepostojeću datoteku
$file = fopen("sample.txt", "r");
?>

Ako datoteka ne postoji, mogli biste dobiti ovakvu grešku:


Warning: fopen(sample.txt) [function.fopen]: failed to open stream: No such file or directory in C:\wamp\www\project\test.php on line 2


Ako slijedimo neke jednostavne korake, možemo spriječiti korisnike da dobiju takvu poruku o grešci.

<?php
if(file_exists("sample.txt")){
    $file = fopen("sample.txt", "r");
} else{
    die("Greška: Datoteka koju pokušavate pristupiti ne postoji.");
}
?>

Greška: Datoteka koju pokušavate pristupiti ne postoji.


Kao što možete primijeniti jednostavnom provjerom postoji li datoteka prije nego što joj pokušate pristupiti, možemo generisati poruku o grešci koja je značajnija za korisnika. Gore korištena funkcija die() jednostavno prikazuje prilagođenu poruku o grešci i prekida trenutnu skriptu ako datoteka 'sample.txt' nije pronađena.



Stvaranje prilagođenog rukovaoca greškama

Možete stvoriti vlastitu funkciju obrađivača grešaka kako biste se bavili greškom u vremenu izvođenja generisanom od strane PHP engin-a. Prilagođeni rukovatelj greškama pruža vam veću fleksibilnost i bolju kontrolu nad greškama, može pregledati grešku i odlučiti što učiniti s njom, može prikazati poruku korisniku, prijaviti grešku u datoteku ili bazu podataka ili poslati e-poštom -mail, pokušajte riješiti problem i nastavite, zatvorite izvršavanje skripte ili ignorišete grešku u potpunosti. Prilagođena funkcija rukovaoca greškama mora biti u stanju obraditi najmanje dva parametra (errno i errstr), ali po želji može prihvatiti dodatna tri parametra (errfile, errline i errcontext), pogledajmo u nastavku šta to znači:

Parametar Opis
errno Određuje nivo greške, kao cijeli broj. Ovo odgovara odgovarajućoj konstanti nivoa greške (E_ERROR, E_WARNING i tako dalje)
errstr Navodi poruku o grešci kao string
errfile Navodi naziv datoteke skripte u kojoj se dogodila greška kao string
errline Određuje broj reda na kojem se dogodila greška kao string
errcontext Navodi niz koji sadrži sve varijable i njihove vrijednosti koje su postojale u trenutku kada se dogodila greška. Korisno za otklanjanje grešaka

Evo primjera jednostavne prilagođene funkcije rukovanja greškama. Ovaj rukovatelj, customError() pokreće se kad god se dogodi greška, bez obzira koliko je trivijalan. Zatim u pretraživaču izbacuje detalje greške i zaustavlja izvršavanje skripte.

<?php
// Funkcija rukovaoca greškama
function customError($errno, $errstr){
    echo "<b>Greška:</b> [$errno] $errstr";
}
?>

Morate reći PHP-u da koristi vašu prilagođenu funkciju obrađivača grešaka - samo pozovite ugrađenu funkciju set_error_handler(), unoseći ime funkcije.

<?php
// Funkcija rukovaoca greškama
function customError($errno, $errstr){
    echo "<b>Greška:</b> [$errno] $errstr";
}
 
// Postavi obrađivač grešaka
set_error_handler("customError");
 
// Greška okidača
echo($test);
?>


Prijava grešaka

Prijavite poruke o greškama u tekstualnu datoteku. Detalje o grešci možete prijaviti i u datoteku dnevnika, ovako:

<?php
function calcDivision($dividend, $divisor){
    if($divisor == 0){
        trigger_error("calcDivision(): Djelilac ne može biti nula", E_USER_WARNING);
        return false;
    } else{
        return($dividend / $divisor);
    }
}
function customError($errno, $errstr, $errfile, $errline, $errcontext){
    $message = date("Y-m-d H:i:s - ");
    $message .= "Greška: [" . $errno ."], " . "$errstr in $errfile on line $errline, ";
    $message .= "Variables:" . print_r($errcontext, true) . "\r\n";
    
    error_log($message, 3, "logs/app_errors.log");
    die("Došlo je do problema, pokušajte ponovo.");
}
set_error_handler("customError");
echo calcDivision(10, 0);
echo "Ovo se nikada neće štampati.";
?>


Pošaljite poruke o grešci e-poštom

Takođe možete poslati e-poštu s detaljima greške koristeći istu funkciju error_log().

<?php
function calcDivision($dividend, $divisor){
    if ($divisor == 0){
        trigger_error("calcDivision(): Djelilac ne može biti nula", E_USER_WARNING);
        return false;
    } else{
        return($dividend / $divisor);
    }
}
function customError($errno, $errstr, $errfile, $errline, $errcontext){
    $message = date("Y-m-d H:i:s - ");
    $message .= "Greška: [" . $errno ."], " . "$errstr in $errfile on line $errline, ";
    $message .= "Variables:" . print_r($errcontext, true) . "\r\n";
    
    error_log($message, 1, "webmaster@example.com");
    die("Došlo je do problema, pokušajte ponovo. Izvještaj o grešci predan webmasteru.");
}
set_error_handler("customError");
echo calcDivision(10, 0);
echo "Ovo se nikada neće štampati.";
?>


Pokretanje greške (Trigger an Error)

Iako PHP mehanizam pokreće grešku kad god naiđe na problem s vašom skriptom, greške možete pokrenuti i sami. To može pomoći da vaša aplikacija postane robusnija jer može označiti potencijalne probleme prije nego što se pretvore u ozbiljne greške. Da biste pokrenuli grešku unutar svoje skripte, pozovite funkciju trigger_error(), predajući poruku o grešci koju želite generisati:

trigger_error("Došlo je do problema.");

Pogledajmo sljedeću funkciju koja izračunava dijeljenje dva broja.

<?php
function calcDivision($dividend, $divisor){
    return($dividend / $divisor);
}
 
// Pozivanje funkcije
echo calcDivision(10, 0);
?>

Ako se kao parametar $ djelioca preda vrijednost nula (0), greška koju generiše PHP mehanizam izgledat će otprilike ovako:


Warning: Division by zero in C:\wamp\www\project\test.php on line 3


Ova poruka ne izgleda baš informativno. Razmotrite sljedeći primjer koji koristi funkciju trigger_error() za generisanje greške.

<?php
function calcDivision($dividend, $divisor){
    if($divisor == 0){
        trigger_error("Djelilac ne može biti nula", E_USER_WARNING);
        return false;
    } else{
        return($dividend / $divisor);
    }
}
 
// Pozivanje funkcije
echo calcDivision(10, 0);
?>

Sada skripta generiše ovu poruku o grešci:


Warning: Djelilac ne može biti nula C:\wamp\www\project\test.php on line 3


Kao što vidite, poruka o grešci generisana u drugom primjeru objašnjava problem jasnije u odnosu na prethodni.