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;
}