PHP Tutorial.nl

Word een PHP expert!!!

Home » Exceptions

Efficiënt fouten afhandelen in PHP: Een gids voor exceptions

We schrijven mooie code en in de meeste gevallen loopt de flow hiervan zoals verwacht. Er zijn echter situaties dat we uitzonderingen op deze flow willen maken. Bijvoorbeeld als we te maken hebben met input, waar we geen invloed hebben. In die gevallen willen we niet de flow breken, maar een Exception geven.  

Een Exception is een uitzondering. Er gebeurd iets dat we niet hebben verwacht, dus gooien we een exception. De uitvoer van het programma wordt dan gestopt, tot de uitzondering is afgehandeld.

In het volgende voorbeeld hebben we een class voor een bankrekening. Hier staat een bepaald saldo op. We schrijven geld af te schrijven met de methode schrijfAf

class BankRekening {

   public $saldo = 0;

   public function __construct( $saldo ) {
      $this->saldo = $saldo;
   }

   public function schrijfAf( $bedrag ) {
      $nieuwSaldo = $this->saldo - $bedrag;
      if ( $nieuwSaldo => 0 ) {  
         $this->saldo = $nieuwSaldo;
      } 
   } 
}

Het afschrijven mag niet leiden tot een negatief saldo. Er wordt dus niets gedaan als het saldo negatief wordt. We moeten extra logica  toevoegen om de gebruiker hiervan op de hoogte te stellen.. 

<?php
$bankRekening = new BankRekening( 10 );

$bedrag = 20;
if( $bankRekening->saldo - $bedrag < 0 ) {
   echo 'Je hebt te weinig saldo';
} 
else {
   $bankRekening->schrijfAf( $bedrag ); 
   echo 'Afgeschreven!';
}

In dit (extreme) geval is extra logica buiten de class geschreven. Het functioneert, maar we zouden het eleganter kunnen maken met een Exception.

Exception geven

Een uitzondering moet gegooid worden. We doen dat door throw new Exception( 'bericht' ); te doen. Het mag ook een subclass van exception zijn. Bijvoorbeeld als er een eigen Exception geschreven is. Hoe specifieker hoe beter.

In ons voorbeeld van de schrijfAf methode kunnen we een Exception gooien als het saldo te laag is. 

   public function schrijfAf( $bedrag ) {
      $nieuwSaldo = $this->saldo - $bedrag;
      if ( $nieuwSaldo < 0 ) { 
         throw new Exception( 'Saldo is te laag' ); 
      } 

      $this->saldo = $nieuwSaldo;
   } 

We gooien nu de uitzondering als het saldo te laag is, maar er gebeurt nog niets mee. Behalve dan dat de verdere uitvoering van de code wordt gestopt, omdat er een Exception is die niet is afgevangen. 

Specifieke Exceptions

Het is een goede gewoonte om specifieke exceptions te gebruiken in plaats van de algemene Exception class. Dit helpt om preciezer te zijn in het afhandelen van fouten en maakt de code beter leesbaar en onderhoudbaar. Bijvoorbeeld, in plaats van een algemene Exception, kun je een SaldoTeLaagException definiëren voor ons bankrekening voorbeeld:

class SaldoTeLaagException extends Exception {}

public function schrijfAf( $bedrag ) {
   $nieuwSaldo = $this->saldo - $bedrag;
   if ( $nieuwSaldo < 0 ) { 
      throw new SaldoTeLaagException( 'Saldo is te laag' ); 
   } 
   $this->saldo = $nieuwSaldo;
}

Door gebruik te maken van specifieke exceptions kun je gerichter reageren op verschillende foutscenario’s in je catch blokken:

try {
   $bedrag = 20;
   $bankRekening->schrijfAf( $bedrag ); 
   echo 'Afgeschreven!';
}
catch( SaldoTeLaagException $e ) {
   echo 'Fout: ' . $e->getMessage();
}
catch( Exception $e ) {
   // Schrijf de fout weg naar een logbestand.
}

Exception Messages

Het is belangrijk om duidelijke en betekenisvolle foutberichten te geven bij het gooien van exceptions. Dit helpt niet alleen bij het debuggen, maar ook bij het informeren van de gebruiker over wat er mis is gegaan. Zorg ervoor dat de berichten informatief zijn en relevante details bevatten.

throw new Exception( 'Saldo is te laag. Huidig saldo: ' . $this->saldo );

Loggen van Exceptions

Logging van exceptions is essentieel voor het monitoren en debuggen van applicaties. Het biedt inzicht in wat er misgaat in de productieomgeving. Gebruik logbestanden of logging frameworks om exceptions vast te leggen.

catch( Exception $e ) {
   error_log( $e->getMessage() );
   // Eventueel meer logging logica
}

Door exceptions te loggen, kun je later de logbestanden analyseren om patronen te ontdekken en zo structurele problemen in de code op te lossen.

Try en catch

Om eventuele fouten af te vangen, moeten we in de code aangeven dat iets uitgevoerd moet worden. We doen dit door try en catch. We  schrijven de uit te voeren code in een try block en vangen een eventuele opgegooide uitzondering. De uitvoering van de code gaat nu gewoon door, omdat de Exception is afgehandeld.

Hoe zit dit eruit in ons voorbeeld:

<?php
$bankRekening = new BankRekening( 10 );

try {
   $bedrag = 20;
   $bankRekening->schrijfAf( $bedrag ); 

   echo 'Afgeschreven!';
}
catch( Exception $e ) {
   echo $e->getMessage();
}  

Het is ook mogelijk om meerdere catch blokken te hebben. Dit is handig als we een subclass van Exception gebruiken.

Stel dat schrijfAf een BankRekeningException gooit in plaats van een Exception. Dan kunnen we ervoor kiezen om niets te doen met iets van een Exception zelf.

<?php
$bankRekening = new BankRekening( 10 );

try {
   $bedrag = 20;
   $bankRekening->schrijfAf( $bedrag ); 

   echo 'Afgeschreven!';
}
catch( BankRekeningException $e ) {
   echo $e->getMessage();
}
catch( Exception $e ) {
   // Schrijf de fout weg naar een logbestand.
}   

Door het gebruik van specifiekere uitzonderingen en het afvangen hiervan hebben we een betere controle op wat de gebruiker te zien krijgt. In het bovenstaande voorbeeld krijgt de gebruiker geen foutmelding te zien als er bijvoorbeeld iets mis is met de database.

Finally

Naast de try en catch is er ook nog finally. De inhoud hiervan wordt altijd uitgevoerd. In het  volgende voorbeeld wordt altijd het saldo getoond.

<?php
$bankRekening = new BankRekening( 10 );

try {
   $bedrag = 20;
   $bankRekening->schrijfAf( $bedrag ); 

   echo 'Afgeschreven!';
}
catch( BankRekeningException $e ) {
   echo $e->getMessage();
}
finally {
   echo 'Je saldo is op dit moment: ' . $bankRekening->saldo;
}