MySQL Java JavaScript PHP Python HTML-CSS C-sharp C++ Go

Tratarea excepțiilor

În timpul rulării unui program pot apărea diverse erori care pot întrerupe funcționarea programului. De exemplu, să luăm următoarea situație:

$a = 5;
$b = 0;
$result = $a / $b;
echo $result;
echo "Sfârșitul programului";

Programul afișează rezultatul împărțirii. Deoarece împărțitorul este 0, iar împărțirea la zero nu este permisă, la execuția împărțirii, programul se va opri, și în browser vom vedea ceva de genul:

Fatal error: Uncaught DivisionByZeroError: Division by zero in C:\localhost\hello.php:11 Stack trace: #0 {main} thrown in C:\localhost\hello.php on line 11

Browserul ne va afișa eroarea apărută și, după linia cu împărțirea, programul nu va mai fi executat.

Cineva ar putea spune că situația este artificială, deoarece noi înșine am definit împărțitorul egal cu zero. Dar datele pot fi primite din exterior. În plus, pe lângă împărțirea la zero, există diverse alte situații în care pot apărea erori. Totuși, PHP oferă o serie de posibilități pentru tratarea acestor situații.

Pentru a trata excepțiile în PHP se folosește construcția try-catch:

try
{
   // codul care poate genera o excepție
}
catch(Tip_excepție $ex)
{
   // tratarea excepției
}

Această construcție, în varianta sa generală, constă din două blocuri - try și catch. În blocul try se plasează codul care potențial poate genera o excepție.

ar în blocul catch se plasează tratarea excepției apărute. Pentru fiecare tip de excepție putem defini o logică de tratare diferită. Tipul concret al excepției pe care dorim să o tratăm se specifică în paranteze după operatorul catch:

catch(Tip_excepție $ex)

După numele tipului se specifică o variabilă de acest tip (în acest caz $ex), care va conține informații despre excepție și pe care o putem folosi la tratarea acesteia.

Dacă în blocul try, la execuția codului, apare o eroare, blocul try oprește execuția și transferă controlul către blocul catch, care tratează eroarea. După finalizarea execuției codului din blocul catch, programul continuă să execute instrucțiunile plasate după blocul catch.

Dacă în blocul try, la execuția codului, nu apar erori, blocul catch nu se execută, iar după finalizarea blocului try, programul continuă să execute instrucțiunile plasate după blocul catch.

De exemplu, să tratăm eroarea împărțirii la zero:

try
{
   // codul care poate genera o excepție
   $a = 5;
   $b = 0;
   $result = $a / $b;
   echo $result;
}
catch(DivisionByZeroError $ex)
{
   // tratarea excepției
   echo "A apărut o excepție:<br>";
   echo $ex . "<br>";
}
echo "Sfârșitul programului";

În acest caz, codul de împărțire la zero, deoarece poate genera o eroare, este plasat în blocul try.

În blocul catch se tratează eroarea de tip DivisionByZeroError, care se generează la împărțirea la zero. Întreaga tratare constă în afișarea informației pe ecran.

În cele din urmă, la execuție, programul va afișa următoarele:

A apărut o excepție:
DivisionByZeroError: Division by zero in C:\localhost\hello.php:14 Stack trace: #0 {main}
Sfârșitul programului

După cum se vede din afișarea programului, acesta nu se oprește brusc la împărțirea la zero, ci continuă execuția.

Tipuri de erori și excepții

În PHP există mai multe tipuri care descriu diverse situații de eroare. Toate aceste tipuri încorporate implementează interfața Throwable:

Toate tipurile se împart în două grupuri: erorile propriu-zise (clasa Error) și excepțiile propriu-zise (clasa Exception). Clasele de erori și excepții care descriu situații concrete moștenesc din clasele Error și Exception.

De exemplu, clasa ArithmeticError moștenește din clasa Error și descrie erorile care apar la efectuarea operațiilor aritmetice. Iar clasa DivisionByZeroError moștenește din ArithmeticError și reprezintă eroarea de împărțire la zero.

Blocul catch

Construcția try..catch permite definirea mai multor blocuri catch pentru a trata diverse tipuri de erori și excepții:

try
{
   $result = 5 / 0;
   echo $result;
}
catch(ParseError $p)
{
   echo "A apărut o eroare de parsare";
}
catch(DivisionByZeroError $d)
{
   echo "Împărțirea la zero nu este permisă";
}

La apariția unei erori, pentru tratarea ei se va selecta blocul catch corespunzător tipului erorii. Astfel, în acest caz, la împărțirea la zero se va executa al doilea bloc catch.

Dacă în blocul try ar apărea o eroare care nu corespunde tipurilor din blocurile catch (în acest caz - tipurile DivisionByZeroError și ParseError), o astfel de eroare nu ar fi tratată, și, în consecință, programul s-ar opri brusc.

Blocurile catch cu tipuri mai specifice de erori și excepții trebuie să fie plasate la început, iar cele cu tipuri mai generale - la sfârșit:

try
{
   $result = 5 / 0;
   echo $result;
}
catch(DivisionByZeroError $ex)
{
   echo "Împărțirea la zero nu este permisă";
}
catch(ArithmeticError $ex)
{
   echo "Eroare la efectuarea operațiilor aritmetice";
}
catch(Error $ex)
{
   echo "A apărut o eroare";
}
catch(Throwable $ex)
{
   echo "Eroare la execuția programului";
}

Clasa DivisionByZeroError se moștenește din ArithmeticError, care la rândul său se moștenește din Error, care implementează interfața Throwable. De aceea, clasa DivisionByZeroError reprezintă un tip mai specific și erorile reprezentate de această clasă trebuie tratate mai întâi.

Tipul Throwable reprezintă cel mai general tip, deoarece toate erorile și excepțiile posibile îi corespund, așa că blocurile catch cu acest tip trebuie plasate la sfârșit.

În acest caz, în blocul try apare o eroare de împărțire la zero. Dar această eroare corespunde tuturor celor patru blocuri catch. PHP va alege primul bloc care corespunde tipului de eroare, în acest caz, blocul pentru tratarea erorii de tip DivisionByZeroError.

Dacă trebuie să tratăm în principiu toate erorile și excepțiile, putem defini doar tratarea tipului general pentru toate, adică Throwable:

try
{
   $result = 5 / 0;
   echo $result;
}
catch(Throwable $ex)
{
   echo "Eroare la execuția programului";
}

Începând cu versiunea PHP 8.0, în blocul catch putem specifica doar tipul excepției tratate, fără a defini o variabilă:

catch(DivisionByZeroError)
{
   echo "A apărut o excepție: împărțirea la zero";
}

Obținerea informațiilor despre erori și excepții

Interfața Throwable oferă o serie de metode care permit obținerea unor informații despre excepția apărută:

  • getMessage(): returnează mesajul de eroare
  • getCode(): returnează codul excepției
  • getFile(): returnează numele fișierului în care a apărut eroarea
  • getLine(): returnează numărul liniei în care a apărut eroarea
  • getTrace(): returnează trasarea stivei
  • getTraceAsString(): returnează trasarea stivei sub formă de șir de caractere

Aplicăm unele dintre aceste metode:

try
{
   $result = 5 / 0;
   echo $result;
}
catch(DivisionByZeroError $ex)
{
   echo "Mesaj de eroare: " . $ex->getMessage() . "<br>";
   echo "Fișier: " . $ex->getFile() . "<br>";
   echo "Număr linie: " . $ex->getLine() . "<br>";
}

Rezultatul execuției:

Mesaj de eroare: Division by zero
Fișier: D:\localhost\hello.php
Număr linie: 11

Blocul finally

Construcția try..catch poate defini și un bloc finally. Acest bloc se execută la sfârșit - după blocul try și catch, indiferent dacă a apărut sau nu o eroare. Adesea, blocul finally este folosit pentru închiderea resurselor utilizate în blocul try.

try
{
   $result = 5 / 0;
   echo $result . "<br>";
}
catch(Throwable $ex)
{
   echo "Eroare la execuția programului<br>";
}
finally
{
   echo "Blocul finally<br>";
}
echo "Sfârșitul programului";

Rezultatul programului:

Eroare la execuția programului
Blocul finally
Sfârșitul programului

Construcția try..catch..finally poate conține fie toate cele trei blocuri, fie doar două blocuri: try și fie blocul catch, fie blocul finally.