Met de komst van php 5, heeft php een nieuwe manier geïntroduceerd waarmee je fouten kan afhandelen in je code. Deze manier is het opvangen van exceptions, welke jouw code genereert. Voor iedereen die al eens met een object georiënteerde taal heeft geprogrammeerd, zoals met JAVA, zal deze wijze totaal niet vreemd zijn.
Om uit te leggen hoe je van deze wijze van fouten afhandeling gebruik kan maken, zal ik dit uitleggen aan de hand van een programmeervoorbeeld. In dit programmeervoorbeeld haal ik uit een tabel met berichten, de berichttitel, in dit geval gaan we van een phpBB tabel uit.
Hoe doen we het nu
Dit artikel gaat er van uit dat je kennis hebt van hoe object georienteerd programmeren in php werkt. Zo niet is het handig om eerst dit artikel aandachtig door te nemen.
Allereerst begin ik met de manier waarop normaal in PHP fouten die optreden worden afgehandeld. Hiervoor heb ik een bestand gemaakt met een aantal aangepaste SQL functies, om de omvang van het bericht te minimaliseren zal ik steeds naar dit SQL bestand verwijzen. We beginnen met ons eerste sql_bestand.
Onderstaande code zal uit de database de onderwerpen van de berichten halen, en deze weergeven op je pagina.
<?php
include('sql_none.php');
$db = 'phpbb';
$host = 'localhost';
$pass = '';
$user = 'root';if (!($connection = connect($user, $pass, $host, $db)))
{
die('Failed to connect to DB');
}$sql = 'SELECT post_subject FROM phpbb_posts';if (!($result = sql_query($sql, $connection)))
{
die('Failed to query DB');
}
while ($row = sql_fetch_array($result))
{
echo $row['post_subject'] . '
';
}
if (!sql_close($connection))
{
die('Failed to close DB');
}
exit;
?>
Door gebruik te maken van de false uitkomsten van de verschillende SQL functies, kunnen we controleren of een functie gelukt is of niet. Wat hier gelijk opvalt, is dat de foutverwerking in de code zit verweven door middel van de if-then-else statements. Doordat de fouten verwerking binnen in je code zit, kan dit soms er voor zorgen dat je code niet heel duidelijk wordt. Helaas is bovenstaande code niet dermate complex om dit te kunnen weergeven. Het idee achter exceptions is dus ook onder andere het scheiden van foutafhandeling van je daadwerkelijke code geweest. Verder kun je het ook heel handig gebruiken om je fouten te identificeren. We gaan nu verder in op hoe je Exceptions zou kunnen toepassen op bovenstaande code.
Implementeren van exceptions
Om beter te begrijpen wat het volgende codevoorbeeld doet, is het eerst belangrijk om te weten wat er nou precies gebeurd wanneer er een Exception optreed. De constructie die achter Exceptions zit is de volgende:
- Je voert een functie uit.
- Er treed een fout op in de functie, de functie gooit hierop een Exception. Keyword hierbij is
throw. - Je script gaat op zoek naar de eerst volgende Exception vanger. Keyword hierbij is
catch. - Je script voert de code uit, die bij deze Exception opvanger staat.
- Je script gaat hieronder verder.
Belangrijk hier om te weten is, dat alle code tussen de functie die de Exception laat optreden en de opvanger van de Exception niet wordt uitgevoerd. Maar wat is nu precies die Exception. Exception is een class binnen php. Wanneer er een fout optreed in je functie, maak je een instance aan van de Exception class, waarna je deze class laat gooien. In je php code ziet dat er op de volgende wijze uit:
if ($error) throw new Exception('Een fout');
Wanneer je error true is, zal deze code een Exception gooien, met de door jouw ingevoerde foutmelding in de vorm van een string. Het is niet verplicht om een string mee te geven aan de functie, maar dit is voor jezelf wel handig voor het debuggen. Wanneer we deze code implementeren levert ons dat het volgende sql_bestand op. In plaats van dat we return false gebruiken om een fout aan te geven, gooien we nu een exception. Wanneer de exception gegooid wordt, zal de functie stoppen en zal in de code gezocht worden naar de eerst volgende Exception vanger.
Opvangen van Exceptions
We hebben nu dus functies die Exceptions gooien, wanneer er iets fout gaat. Echter hebben we daar niets aan als we deze ook niet kunnen opvangen. De code om dit te doen ziet er ’schematisch’ ongeveer het volgende uit: Probeer { code } vang op { exception }.
In php code ziet dit er als volgt uit:
try
{
do_stuff();
}
catch (Exception $e)
{
echo $e->getMessage();
}
Er wordt geprobeerd om de code die in het try gedeelte staat, uit te voeren. Wanneer er echter een een Exception optreed zal Exception optreed, zal het script springen naar de eerst volgende catch statement wat bij die Exception hoort en de code die hierin staat uitvoeren. Wanneer we dit gaan toepassen op ons vorige code voorbeeld, ziet onze code er als volgt uit:
<?php
include('sql_general.php');
try
{
$db = 'phpbb';
$host = 'localhost';
$pass = '';
$user = 'root';$connection = connect($user, $pass, $host, $db);$sql = 'SELECT post_subject FROM phpbb_posts';$result = sql_query($sql, $connection);
while ($row = sql_fetch_array($result))
{
echo $row['post_subject'] . '
';
}
sql_close($connection);
}
catch (Exception $e)
{
echo 'Caught: ' .$e->getMessage(). '
';
}
?>
Het eerste wat opvalt is dat nu de foutafhandeling compleet los staat van de rest van je code, wat resulteert in beter leesbare code wanneer je aan de slag gaat met complexere scripts. Daarnaast zien we ook een bepaalde functie naar voren komen welke onderdeel is van de Exception class. getMessage() print de fout string, die jij in je Exception declaratie hebt meegegeven. Hiermee kan je bijvoorbeeld aangeven waar je Exception is opgetreden.
Al met al bied bovenstaande code een heleboel leuke mogelijkheden, echter is dit nog niet erg handig nog in gebruik, omdat ik hier alleen maar een tekst string heb die mij verteld welke foutmelding er precies is geweest. Alle foutmeldingen zijn van het type Exception en dat is slechts een type.
Geavanceerde technieken
En nu komt dan eindelijk een van de grote voordelen om de hoek kijken die het object georiënteerd programmeren met zich meebrengt, namelijk het feit dat ik een class kan uitbreiden. In mijn bovenstaande code voorbeeld zou je bijvoorbeeld kunnen denken dat het handig is om de volgende foutmeldingen te kunnen waarnemen:
- Connectie fout
- Database selectie fout
- Etc.
We kunnen dus onze eigen Exceptions creëren, bijvoorbeeld:
class ConnectException extends Exception { }
Maar dat is niet het enige. We kunnen ook meerdere catch clausules onder elkaar maken, waarmee we de verschillende Exceptions kunnen opvangen, bijvoorbeeld:
catch (FirstException $e) { echo $e->getMessage(); }
catch (SecondException $e) { echo $e->getMessage(); }
Hiermee kunnen we per fout dus een apart stukje code aanroepen dat afgehandeld wordt wanneer een bepaalde fout optreed. We kunnen dus nu ons sql_bestand aanpassen en deze eigen foutmeldingen toepassen. En wanneer we ook ons script aanpassen komt het er als volgt uit te zien:
<?php
include('sql_costumised.php');
try
{
$db = 'phpbb';
$host = 'loalhost';
$pass = '';
$user = 'root';$connection = connect($user, $pass, $host, $db);$sql = 'SELECT post_subject FROM phpbb_posts';$result = sql_query($sql, $connection);
while ($row = sql_fetch_array($result))
{
echo $row['post_subject'] . '
';
}
sql_close($connection);
}
catch (SelectConnectException $e)
{
// eerst deze afvangen, want anders vangt de parent van deze class (Connect Exception) deze error op.
echo 'Caught: ' .$e->getMessage(). '
';
echo 'Handel hier dingen af wanneer je een fout hebt bij het selecteren van de DB.';
// Een voorbeeld van een handeling zou de volgende kunnen zijn: er is nog een db verbinding open, dus sluit deze.
mysql_close();
}
catch (ConnectException $e)
{
echo 'Caught: ' .$e->getMessage(). '
';
echo 'Handel hier dingen af wanneer je fouten krijgt bij de verbinding met de database server.';
}
catch (QueryException $e)
{
echo 'Caught: ' .$e->getMessage(). '
';
echo 'Handel hier dingen af, wanneer je sql query fouten oplevert.';
// Ook hier zou je eventueel de database verbinding nog kunnen verbreken, immers deze verbinding is er wel omdat bovenstaande
// Exceptions niet zijn geweest.
}
catch (CloseException $e)
{
echo 'Caught: ' .$e->getMessage(). '
';
echo 'Handel hier dingen af, wanneer je de database verbinding niet correct afsluit.';
}
// Vang alle overige Exceptions
catch (Exception $e)
{
// Exception is de parent van ALLE exceptions, dus deze zal alle overige exceptions afvangen.
// Gebruik deze dus ook ALTIJD aan het einde van je catch lijst, zodat je specifieke fouten wel kan opvangen.
echo 'Caught: ' .$e->getMessage(). '
';
}
?>
De exception opvangers zien er nu zeer complex uit, maar bovenstaande constructie bied een legio aan mogelijkheden. Zo kun je bepaalde stukken code uitvoeren wanneer je een bepaalde fout krijgt, je kunt bijvoorbeeld de sql verbinding afbreken.
Belangrijk is het wel dat wanneer je je eigen Exceptions gaat creëren, dat je wel al je Exceptions die kunnen optreden, ook daadwerkelijk opvangt. Immers het php script zal pas verdergaan met de code, na je exception opvanger.
Ik hoop dat ik jullie hiermee een beetje een introductie heb kunnen geven in deze nieuwe manier van het opvangen van fouten die optreden tijdens het uitvoeren van je code en hoop natuurlijk dat dit je helpt bij het schrijven van betere code voor de toekomst.
Voor meer informatie over hoe Exceptions werken, kijk je het best even op de site van php.net over Exceptions.
Tags: Exceptions, foutafhandeling, geavanceerde technieken, php
leuke tut!
erg verhelderend (vooral voor mensen die hier nog nooit mee gewerkt hebben
)!
als iemand de code niet helemaal snapt, tip: zet het in je textverwerker en voeg zelf tabs toe (dat kan niet met dit blog), dan is het misschien iets duidelijker