Tutorials
Foutafhandeling in PHP (Error Handling)
De basis van ieder PHP script!
Pagina 1
Inleiding
Het allereerste begin met PHP is vrij simpel. Gewapend met een goede PHP editor en een lokale testserver is het voor niemand een probleem om zijn eerste scriptjes in elkaar te zetten. Van het eenvoudige echoën van 'Hello World' tot het verwerken van een eerste formuliertje of zelfs al een simpele verbinding met een database. Iedere programmeur, professioneel of als hobby, is op dezelfde manier begonnen.
Helaas wordt met deze grote nieuwe mogelijkheden die ontdekt worden een verlangen aangewakkerd om zo snel mogelijk grote mooie scripts te schrijven. En helaas is het maar al te vaak zo dat beginnende programmeurs vastlopen omdat de basis van elk goed PHP script, de foutafhandeling, uit het oog verloren wordt.
Foutafhandeling (engels: Error Handling) vormt de basis van ieder PHP script. Zowel tijdens de ontwikkelfase, waarin foutafhandeling noodzakelijk is om goed te kunnen debuggen, als wanneer het script voltooid is, mag foutafhandeling nooit ontbreken.
In deze tutorial zal ik verschillende manieren van foutafhandeling bespreken die beschikbaar zijn in PHP. Ik zal veel gebruik maken van voorbeelden die variëren van het eenvoudige gebruik van de functie die() tot het afhandelen van fouten in een object georiënteerde context met behulp van de Exception klasse.
Benodigde voorkennis:
[li]Basiskennis PHP[/li]
Helaas wordt met deze grote nieuwe mogelijkheden die ontdekt worden een verlangen aangewakkerd om zo snel mogelijk grote mooie scripts te schrijven. En helaas is het maar al te vaak zo dat beginnende programmeurs vastlopen omdat de basis van elk goed PHP script, de foutafhandeling, uit het oog verloren wordt.
Foutafhandeling (engels: Error Handling) vormt de basis van ieder PHP script. Zowel tijdens de ontwikkelfase, waarin foutafhandeling noodzakelijk is om goed te kunnen debuggen, als wanneer het script voltooid is, mag foutafhandeling nooit ontbreken.
In deze tutorial zal ik verschillende manieren van foutafhandeling bespreken die beschikbaar zijn in PHP. Ik zal veel gebruik maken van voorbeelden die variëren van het eenvoudige gebruik van de functie die() tot het afhandelen van fouten in een object georiënteerde context met behulp van de Exception klasse.
Benodigde voorkennis:
[li]Basiskennis PHP[/li]
Pagina 2
Weergave en interpretatie van PHP fouten
Voordat we daadwerkelijk kunnen beginnen met het toepassen van foutafhandeling is het van belang dat PHP ook daadwerkelijk fouten weergeeft. Om hiervoor te zorgen plaatsen we van nu af aan het volgende stukje code boven elk script:
De eerste regel zorgt ervoor dat de 'display_errors' instelling in php.ini aangezet wordt waardoor PHP fouten zal weergeven. De tweede regel zorgt ervoor dat alle mogelijke fouten die optreden worden weergegeven. Later in deze tutorial bespreek ik de mogelijke instellingen die je kunt doen.
Verschillende foutmeldingen
Er zijn verschillende foutmeldingen die PHP terug kan geven, maar de standaard foutmeldingen hebben altijd dezelfde syntax:
Mocht je nog niet bekend zijn met de foutmeldingen die PHP genereert, dan zal daar in de loop van deze tutorial zeker verandering in komen. Hetgeen voor nu belangrijk is, zijn de verschillende soorten fouten die op kunnen treden:
[li]Parse error - Er zit een fout in de syntax van je php script[/li]
[li]Notice - PHP heeft iets gevonden dat een fout zou kunnen zijn, maar niet per se is[/li]
[li]Warning - Een belangrijke maar niet fatale fout. Het script wordt verder uitgevoerd[/li]
[li]Fatal error - Een belangrijke en fatale fout. Het script wordt niet verder uitgevoerd[/li]
Om een en ander wat te verduidelijken volgt hier een scriptje dat een foutmelding zal geven:
De foutmelding die gegeven wordt is een 'parse error':
Aan deze foutmelding zie je dat er op regel 6 in het bestand test.php iets fout gegaan is. PHP meldt dat de echo die op die regel staat niet verwacht wordt. Dat komt overduidelijk doordat de echo op regel 5 niet met een ; afgesloten wordt.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
?>
De eerste regel zorgt ervoor dat de 'display_errors' instelling in php.ini aangezet wordt waardoor PHP fouten zal weergeven. De tweede regel zorgt ervoor dat alle mogelijke fouten die optreden worden weergegeven. Later in deze tutorial bespreek ik de mogelijke instellingen die je kunt doen.
Verschillende foutmeldingen
Er zijn verschillende foutmeldingen die PHP terug kan geven, maar de standaard foutmeldingen hebben altijd dezelfde syntax:
[soort fout]: [foutmelding] in [bestand] on line [regelnummer]
Mocht je nog niet bekend zijn met de foutmeldingen die PHP genereert, dan zal daar in de loop van deze tutorial zeker verandering in komen. Hetgeen voor nu belangrijk is, zijn de verschillende soorten fouten die op kunnen treden:
[li]Parse error - Er zit een fout in de syntax van je php script[/li]
[li]Notice - PHP heeft iets gevonden dat een fout zou kunnen zijn, maar niet per se is[/li]
[li]Warning - Een belangrijke maar niet fatale fout. Het script wordt verder uitgevoerd[/li]
[li]Fatal error - Een belangrijke en fatale fout. Het script wordt niet verder uitgevoerd[/li]
Om een en ander wat te verduidelijken volgt hier een scriptje dat een foutmelding zal geven:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
echo 'Foo'
echo 'Bar';
?>
De foutmelding die gegeven wordt is een 'parse error':
Parse error: syntax error, unexpected T_ECHO, expecting ',' or ';' in C:\wamp\www\test.php on line 6
Aan deze foutmelding zie je dat er op regel 6 in het bestand test.php iets fout gegaan is. PHP meldt dat de echo die op die regel staat niet verwacht wordt. Dat komt overduidelijk doordat de echo op regel 5 niet met een ; afgesloten wordt.
Pagina 3
Basis foutafhandeling met die()
Het eerste voorbeeld van foutafhandeling dat ik zal bespreken is het gebruik van de die() functie. Zoals de naam van deze functie al doet vermoeden, stopt deze functie de uitvoer van de rest van het script en toont een foutmelding op het scherm.
Laten we kijken naar een voorbeeld waarin we met de functie file_get_contents() proberen om de inhoud van een bestand op te halen. Omdat we de fout willen zien, zijn we natuurlijk geinteresseerd in wat er gebeurd als het bestand niet bestaat.
Als bestand.txt niet bestaat, zal de volgende foutmelding gegeven worden:
Nu is dit natuurlijk niet echt een nette foutmelding die je aan je gebruikers zou willen tonen. Dat is als volgt op te lossen:
We controleren nu eerst of het bestand wel bestaat. Als dat niet het geval is stoppen we met die() het script en geven we een foutmelding. De output die we nu op het scherm zien is:
Dit voorbeeld laat zien hoe je op een hele eenvoudige manier, een zelf geschreven foutmelding kunt weergeven. Ook zien we dat de verdere uitvoer inderdaad gestopt wordt, de echo op regel 13 wordt niet uitgevoerd. Later in deze tutorial zullen we veel uitgebreidere voorbeelden zien.
Een ander voorbeeld waar de functie die() veel gebruikt wordt en waar de werking beter tot zijn recht komt, is bij het uitvoeren van een sql query. Als de query mislukt, zal PHP namelijk niet uit zichzelf een foutmelding geven, deze zul je zelf moeten opvragen met de functie mysql_error().
Als de query nu mislukt, en mysql_query() dus FALSE teruggeeft, zal 'or die()' ervoor zorgen dat de foutafhandeling in werking treedt. In dit geval zal de verdere uitvoer van het script gestopt worden en zal er een foutmelding gegeven worden.
Laten we kijken naar een voorbeeld waarin we met de functie file_get_contents() proberen om de inhoud van een bestand op te halen. Omdat we de fout willen zien, zijn we natuurlijk geinteresseerd in wat er gebeurd als het bestand niet bestaat.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
$content = file_get_contents('bestand.txt');
?>
Als bestand.txt niet bestaat, zal de volgende foutmelding gegeven worden:
Warning: file_get_contents(bestand.txt) [function.file-get-contents]: failed to open stream: No such file or directory in C:\wamp\www\test.php on line 5
Nu is dit natuurlijk niet echt een nette foutmelding die je aan je gebruikers zou willen tonen. Dat is als volgt op te lossen:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
if(file_exists('bestand.txt'))
{
$content = file_get_contents('bestand.txt');
}
else
{
die('FOUT: Het gekozen bestand bestaat niet.');
}
echo 'Hello World!';
?>
We controleren nu eerst of het bestand wel bestaat. Als dat niet het geval is stoppen we met die() het script en geven we een foutmelding. De output die we nu op het scherm zien is:
FOUT: Het gekozen bestand bestaat niet.
Dit voorbeeld laat zien hoe je op een hele eenvoudige manier, een zelf geschreven foutmelding kunt weergeven. Ook zien we dat de verdere uitvoer inderdaad gestopt wordt, de echo op regel 13 wordt niet uitgevoerd. Later in deze tutorial zullen we veel uitgebreidere voorbeelden zien.
Een ander voorbeeld waar de functie die() veel gebruikt wordt en waar de werking beter tot zijn recht komt, is bij het uitvoeren van een sql query. Als de query mislukt, zal PHP namelijk niet uit zichzelf een foutmelding geven, deze zul je zelf moeten opvragen met de functie mysql_error().
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
$sql = "SELECT * FROM tabel";
$result = mysql_query($sql) or die('FOUT: '.mysql_error());
?>
Als de query nu mislukt, en mysql_query() dus FALSE teruggeeft, zal 'or die()' ervoor zorgen dat de foutafhandeling in werking treedt. In dit geval zal de verdere uitvoer van het script gestopt worden en zal er een foutmelding gegeven worden.
Pagina 4
Een flexibelere manier: trigger_error()
Zoals op de voorgaande pagina bleek, kan de die() functie gebruikt worden om een vrij ruwe manier van foutafhandeling toe te passen. Het script wordt gestopt en een foutmelding wordt gegeven. Maar vooral in de grotere scripts kan het vervelend zijn als zomaar het script onderbroken wordt, bijvoorbeeld als er nog een deel van de layout getoond moet worden.
De functie trigger_error() biedt ons al meer flexibiliteit. Het eerste grote voordeel is dat de uitvoer van het script gewoon doorgaat. Bekijk nu het volgende voorbeeld:
De output die er nu gegeven wordt:
We zien dat inderdaad de uitvoer van het script gewoon verder gaat. De foutmelding wordt gegeven, maar ook de echo op regel 13 wordt nog uitgevoerd.
De functie trigger_error accepteert ook een tweede parameter. Hiermee kunnen we het soort fout aangeven dat 'getriggerd' moet worden. De volgende parameters zijn gebruikelijk om mee te geven:
[li]E_USER_ERROR - Een fatale fout waarbij de uitvoer van het script gestopt wordt[/li]
[li]E_USER_WARNING - Een niet-fatale fout waarbij de uitvoer niet gestopt wordt[/li]
[li]E_USER_NOTICE - Een mogelijke fout die de uitvoer van het script niet stopt[/li]
Standaard wordt een E_USER_NOTICE getriggerd. Het volgende voorbeeld laat een toepassing van deze tweede parameter zien:
De output is nu als volgt:
We zien dat de verdere uitvoer van het script inderdaad helemaal gestopt is. De echo op regel 13 wordt ook niet meer weergegeven.
De functie trigger_error() biedt ons al meer flexibiliteit. Het eerste grote voordeel is dat de uitvoer van het script gewoon doorgaat. Bekijk nu het volgende voorbeeld:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
if(file_exists('bestand.txt'))
{
$content = file_get_contents('bestand.txt');
}
else
{
trigger_error('FOUT: Het gekozen bestand bestaat niet.');
}
echo 'Hello World';
?>
De output die er nu gegeven wordt:
Notice: FOUT: Het gekozen bestand bestaat niet. in C:\wamp\www\test.php on line 11
Hello World
We zien dat inderdaad de uitvoer van het script gewoon verder gaat. De foutmelding wordt gegeven, maar ook de echo op regel 13 wordt nog uitgevoerd.
De functie trigger_error accepteert ook een tweede parameter. Hiermee kunnen we het soort fout aangeven dat 'getriggerd' moet worden. De volgende parameters zijn gebruikelijk om mee te geven:
[li]E_USER_ERROR - Een fatale fout waarbij de uitvoer van het script gestopt wordt[/li]
[li]E_USER_WARNING - Een niet-fatale fout waarbij de uitvoer niet gestopt wordt[/li]
[li]E_USER_NOTICE - Een mogelijke fout die de uitvoer van het script niet stopt[/li]
Standaard wordt een E_USER_NOTICE getriggerd. Het volgende voorbeeld laat een toepassing van deze tweede parameter zien:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
if(file_exists('bestand.txt'))
{
$content = file_get_contents('bestand.txt');
}
else
{
trigger_error('FOUT: Het gekozen bestand bestaat niet.', E_USER_ERROR);
}
echo 'Hello World';
?>
De output is nu als volgt:
Fatal error: FOUT: Het gekozen bestand bestaat niet. in C:\wamp\www\test.php on line 11
We zien dat de verdere uitvoer van het script inderdaad helemaal gestopt is. De echo op regel 13 wordt ook niet meer weergegeven.
Pagina 5
Foutafhandeling en controle van variabelen
Tot nu toe hebben we het enkel gehad over een aantal verschillende manieren om fouten in een PHP script weer te geven. Voordat ik verder in ga op meer verschillende manieren voor de foutafhandeling in PHP, wil ik eerst aandacht besteden aan het waar en waarom. Dus waar komt foutafhandeling in een PHP script voor en waarom zou je het eigenlijk toepassen.
Zoals ik in de inleiding al vertelde vormt de foutafhandeling de basis van ieder goed PHP script. De eerste reden voor die bewering is het mogelijk maken van het debuggen van je scripts. Als een script niet werkt wil je weten waarom het niet werkt en een gedetailleerde foutmelding is daarbij van belang.
De tweede, maar zeker niet minder belangrijke, reden is de veiligheid van je scripts. In veel gevallen zul je gebruik maken van input van gebruikers in de form van GET of POST variabelen of bijvoorbeeld cookies. Geen van deze input is te vertrouwen en een goede controle is een vereiste. Bij deze controle is het van groot belang dat de foutafhandeling op orde is. Ten eerste wederom om je script te kunnen debuggen, maar ten tweede om de gebruiker een nette foutmelding te kunnen geven als er iets fout gaat.
Controle van gebruikersinput
De controle van input van gebruikers gaat in bijna alle gevallen met behulp van if/else statements en het gebruik van functies als isset() en empty() om te controleren of variabelen bestaan en een waarde hebben.
In dit voorbeeldje wordt een variabele uit een gepost formuliertje verwerkt. De verwerking begint met de controle of het formulier wel gepost is en vervolgens de controle van de variabele $_POST['naam']. Mocht blijken dat deze variabele niet bestaat of dat de variabele leeg is, dan wordt er een foutmelding aan de array $aErrors toegevoegd.
Hoewel we hier geen gebruik maken van functies als die() of trigger_error() valt ook dit onder de foutafhandeling binnen een script. Alleen maken we nu gebruik van een array $aErrors om de foutmeldingen tijdelijk in op te slaan zodat ze verderop in het script getoond kunnen worden.
Foutafhandeling bij sql queries
Een ander punt waar foutafhandeling absoluut niet kan ontbreken, is bij het uitvoeren van sql queries. Zoals ik eerder al zei zal PHP namelijk nooit zelf een foutmelding teruggeven als er iets in een query fout gaat. Je zult altijd moeten controleren of een query gelukt is!
Eerder heb ik al een voorbeeld met die() gegeven, maar zoals gezegd is het waarschijnlijk niet gewenst om direct je script om zeep te helpen als een query mislukt. Vandaar nog een voorbeeld met trigger_error(), een syntax die ikzelf ook veel gebruikt heb.
In bovenstaande code wordt met mysql_query() een sql query uitgevoerd. Als deze mislukt wordt trigger_error() aangeroepen met daarin een foutmelding. In dit geval bestaat de foutmelding uit een combinatie van mysql_error(), welke de foutmelding van MySQL weergeeft, en de query die uitgevoerd is op de database.
Later in deze tutorial, als ik toekom aan het gebruik van 'exceptions', zal ik laten zien hoe je foutafhandeling kunt toepassen als je de connectie met de database bijvoorbeeld via PDO afhandelt.
Uiteraard kun je deze voorbeelden allemaal verder uitbreiden. Denk bijvoorbeeld aan controleren op foutcodes die mysql teruggeeft en aan de hand daarvan verschillende foutmeldingen geven of acties uitvoeren.Of denk aan controles voor bijvoorbeeld een minimum lengte van een gebruikersnaam of de geldigdheid van een email adres. Ook dat zijn allemaal dingen waarbij foutafhandeling een rol spelen, maar ze zijn te specifiek om in de tutorial te behandelen.
Zoals ik in de inleiding al vertelde vormt de foutafhandeling de basis van ieder goed PHP script. De eerste reden voor die bewering is het mogelijk maken van het debuggen van je scripts. Als een script niet werkt wil je weten waarom het niet werkt en een gedetailleerde foutmelding is daarbij van belang.
De tweede, maar zeker niet minder belangrijke, reden is de veiligheid van je scripts. In veel gevallen zul je gebruik maken van input van gebruikers in de form van GET of POST variabelen of bijvoorbeeld cookies. Geen van deze input is te vertrouwen en een goede controle is een vereiste. Bij deze controle is het van groot belang dat de foutafhandeling op orde is. Ten eerste wederom om je script te kunnen debuggen, maar ten tweede om de gebruiker een nette foutmelding te kunnen geven als er iets fout gaat.
Controle van gebruikersinput
De controle van input van gebruikers gaat in bijna alle gevallen met behulp van if/else statements en het gebruik van functies als isset() en empty() om te controleren of variabelen bestaan en een waarde hebben.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
$aErrors = array();
if($_SERVER['REQUEST_METHOD'] == 'POST')
{
if(isset($_POST['naam']) && !empty(trim($_POST['naam'])))
{
$sNaam = trim($_POST['naam']);
}
else
{
$aErrors[] = 'Er is geen geldige naam ingevuld';
}
}
?>
In dit voorbeeldje wordt een variabele uit een gepost formuliertje verwerkt. De verwerking begint met de controle of het formulier wel gepost is en vervolgens de controle van de variabele $_POST['naam']. Mocht blijken dat deze variabele niet bestaat of dat de variabele leeg is, dan wordt er een foutmelding aan de array $aErrors toegevoegd.
Hoewel we hier geen gebruik maken van functies als die() of trigger_error() valt ook dit onder de foutafhandeling binnen een script. Alleen maken we nu gebruik van een array $aErrors om de foutmeldingen tijdelijk in op te slaan zodat ze verderop in het script getoond kunnen worden.
Foutafhandeling bij sql queries
Een ander punt waar foutafhandeling absoluut niet kan ontbreken, is bij het uitvoeren van sql queries. Zoals ik eerder al zei zal PHP namelijk nooit zelf een foutmelding teruggeven als er iets in een query fout gaat. Je zult altijd moeten controleren of een query gelukt is!
Eerder heb ik al een voorbeeld met die() gegeven, maar zoals gezegd is het waarschijnlijk niet gewenst om direct je script om zeep te helpen als een query mislukt. Vandaar nog een voorbeeld met trigger_error(), een syntax die ikzelf ook veel gebruikt heb.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
$sql = "SELECT * FROM tabel";
if(!$res = mysql_query($sql))
{
trigger_error(mysql_error().'<br />In query: '.$sql)
}
else
{
// Rest van het script, fetchen van resultaten bijvoorbeeld.
}
?>
In bovenstaande code wordt met mysql_query() een sql query uitgevoerd. Als deze mislukt wordt trigger_error() aangeroepen met daarin een foutmelding. In dit geval bestaat de foutmelding uit een combinatie van mysql_error(), welke de foutmelding van MySQL weergeeft, en de query die uitgevoerd is op de database.
Later in deze tutorial, als ik toekom aan het gebruik van 'exceptions', zal ik laten zien hoe je foutafhandeling kunt toepassen als je de connectie met de database bijvoorbeeld via PDO afhandelt.
Uiteraard kun je deze voorbeelden allemaal verder uitbreiden. Denk bijvoorbeeld aan controleren op foutcodes die mysql teruggeeft en aan de hand daarvan verschillende foutmeldingen geven of acties uitvoeren.Of denk aan controles voor bijvoorbeeld een minimum lengte van een gebruikersnaam of de geldigdheid van een email adres. Ook dat zijn allemaal dingen waarbij foutafhandeling een rol spelen, maar ze zijn te specifiek om in de tutorial te behandelen.
Pagina 6
Een eigen foutafhandeling functie
Hoewel trigger_error() ons in staat stelt om al veel gedetaileerdere foutmeldingen te geven, wordt er nog steeds gebruik gemaakt van de standaard error handler van PHP. We zitten dus nog altijd vast aan die standaard foutmeldingen van PHP.
Gelukkig biedt PHP ons ook de mogelijkheid om zelf een functie te schrijven die de foutafhandeling voor ons kan verzorgen en die de standaard error handler van PHP kan vervangen. De syntax van zo'n functie moet altijd als volgt zijn:
De verschillende parameters, waarvan de eerste twee verplicht zijn, hebben de volgende waarden:
[li]level - Geeft het level van de fout weer. Zie hieronder voor de waarden[/li]
[li]message - De foutmelding die gegenereerd wordt[/li]
[li]file - De bestandsnaam waarin de fout opgetreden is[/li]
[li]context - Een array waarin alle variabelen en hun waarden ten tijde van de fout[/li]
De waarden van het level van de fout zijn als volgt:
[li]E_WARNING - 2[/li]
[li]E_NOTICE - 8[/li]
[li]E_USER_ERROR - 256[/li]
[li]E_USER_WARNING - 512[/li]
[li]E_USER_NOTICE - 1024[/li]
[li]E_RECOVERABLE_ERROR - 4096[/li]
[li]E_ALL - 8191[/li]
Voor het schrijven van de functie zijn deze waarden niet echt van belang. Zodra de functie namelijk aangeroepen wordt, dat zoals we later zullen zien onder andere ook met trigger_error() kan, worden de parameters van de functie automatisch gevuld.
Laten we eens kijken naar een eenvoudig voorbeeld van een eigen foutafhandeling functie en de toepassing ervan in een script:
De output van dit scriptje is de volgende:
Zoals verwacht wordt er een foutmelding gegenereerd omdat $var niet bestaat. Nu wordt echter in plaats van de standaard php foutmelding, onze eigen foutmelding weergegeven.
Uiteraard is deze melding lang niet zo uitgebreid als de standaard php foutmelding, maar het pricipe moge duidelijk zijn. Een voorbeeld van een iets uitgebreideren functie zie je hieronder:
Met dit script zal de output zijn:
Ook deze functie is eigenlijk nog vrij beperkt, maar ik denk dat het wel een kleine indruk geeft van de mogelijkheden die er zijn.
Zo zou je er bijvoorbeeld voor kunnen kiezen om niet de precieze foutmelding op het scherm te zetten, maar deze via mail te versturen, aan een error log toe te voegen of weg te schrijven naar een database. Zolang je ervoor zorgt dat de syntax van de functie in stand blijft, kun je de foutafhandeling van jouw scripts helemaal naar je eigen wensen aanpassen.
Ontwikkelfase vs. Productiefase
Een belangrijk iets waar je rekening mee moet houden bij het toepassen van een type foutafhandeling, is de fase waarin je website verkeerd. In de ontwikkelfase ben je nog volop bezig met het bouwen en debuggen van je website. In deze fase wil je foutmeldingen dan ook zo gedetailleerd mogelijk op het scherm hebben. In de productiefase daarentegen, de fase waarin het grote publiek toegang heeft tot je website, wil je dat natuurlijk niet. Dan wil je dat de gebruiker enkel een nette foutmelding krijgt en dat de gedetailleerde foutmelding weggeschreven wordt in een log of bijvoorbeeld naar je gemaild wordt.
Dit valt allemaal te regelen met je eigen foutafhandeling functie. Pas deze dus aan of gebruik verschillende functies in de verschillende fases!
Gelukkig biedt PHP ons ook de mogelijkheid om zelf een functie te schrijven die de foutafhandeling voor ons kan verzorgen en die de standaard error handler van PHP kan vervangen. De syntax van zo'n functie moet altijd als volgt zijn:
<?php
function error_functie (level, message, file, line, context)
?>
De verschillende parameters, waarvan de eerste twee verplicht zijn, hebben de volgende waarden:
[li]level - Geeft het level van de fout weer. Zie hieronder voor de waarden[/li]
[li]message - De foutmelding die gegenereerd wordt[/li]
[li]file - De bestandsnaam waarin de fout opgetreden is[/li]
[li]context - Een array waarin alle variabelen en hun waarden ten tijde van de fout[/li]
De waarden van het level van de fout zijn als volgt:
[li]E_WARNING - 2[/li]
[li]E_NOTICE - 8[/li]
[li]E_USER_ERROR - 256[/li]
[li]E_USER_WARNING - 512[/li]
[li]E_USER_NOTICE - 1024[/li]
[li]E_RECOVERABLE_ERROR - 4096[/li]
[li]E_ALL - 8191[/li]
Voor het schrijven van de functie zijn deze waarden niet echt van belang. Zodra de functie namelijk aangeroepen wordt, dat zoals we later zullen zien onder andere ook met trigger_error() kan, worden de parameters van de functie automatisch gevuld.
Laten we eens kijken naar een eenvoudig voorbeeld van een eigen foutafhandeling functie en de toepassing ervan in een script:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
function myErrorHandler($errno, $errstr)
{
echo 'Er is een fout opgetreden<br />';
echo 'Foutlevel: '.$errno.'<br />';
echo 'Foutmelding: "'.$errstr.'"';
}
set_error_handler('myErrorHandler');
// Dit zal een foutmelding opleveren:
echo $var;
?>
De output van dit scriptje is de volgende:
Er is een fout opgetreden
Foutlevel: 8
Foutmelding: "Undefined variable: var"
Zoals verwacht wordt er een foutmelding gegenereerd omdat $var niet bestaat. Nu wordt echter in plaats van de standaard php foutmelding, onze eigen foutmelding weergegeven.
Uiteraard is deze melding lang niet zo uitgebreid als de standaard php foutmelding, maar het pricipe moge duidelijk zijn. Een voorbeeld van een iets uitgebreideren functie zie je hieronder:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
function myErrorHandler($iLevel, $sMessage, $sFile, $iLine)
{
$aLevels = array(
2 => 'WARNING',
8 => 'NOTICE',
256 => 'FATAL ERROR',
512 => 'WARNING',
1024 => 'NOTICE' );
if(array_key_exists($iLevel, $aLevels))
{
$sLevel = $aLevels[$iLevel];
}
else
{
$sLevel = 'ONBEKENDE FOUT';
}
echo 'Er is een fout opgetreden!<br />';
echo 'Foutsoort: '.$sLevel.'<br />';
echo 'Foutmelding: '.$sMessage.'<br />';
echo 'Bestand: '.$sFile.'<br />';
echo 'Regel: '.$iLine.'<br />';
if($iLevel == 256)
{
echo 'Script wordt gestopt...';
exit();
}
}
set_error_handler('myErrorHandler');
// Een foutmeling triggeren:
trigger_error('Dit is een voorbeeld foutmelding', E_USER_ERROR);
?>
Met dit script zal de output zijn:
Er is een fout opgetreden!
Foutsoort: FATAL ERROR
Foutmelding: Dit is een voorbeeld foutmelding
Bestand: C:\wamp\www\test.php
Regel: 39
Script wordt gestopt...
Ook deze functie is eigenlijk nog vrij beperkt, maar ik denk dat het wel een kleine indruk geeft van de mogelijkheden die er zijn.
Zo zou je er bijvoorbeeld voor kunnen kiezen om niet de precieze foutmelding op het scherm te zetten, maar deze via mail te versturen, aan een error log toe te voegen of weg te schrijven naar een database. Zolang je ervoor zorgt dat de syntax van de functie in stand blijft, kun je de foutafhandeling van jouw scripts helemaal naar je eigen wensen aanpassen.
Ontwikkelfase vs. Productiefase
Een belangrijk iets waar je rekening mee moet houden bij het toepassen van een type foutafhandeling, is de fase waarin je website verkeerd. In de ontwikkelfase ben je nog volop bezig met het bouwen en debuggen van je website. In deze fase wil je foutmeldingen dan ook zo gedetailleerd mogelijk op het scherm hebben. In de productiefase daarentegen, de fase waarin het grote publiek toegang heeft tot je website, wil je dat natuurlijk niet. Dan wil je dat de gebruiker enkel een nette foutmelding krijgt en dat de gedetailleerde foutmelding weggeschreven wordt in een log of bijvoorbeeld naar je gemaild wordt.
Dit valt allemaal te regelen met je eigen foutafhandeling functie. Pas deze dus aan of gebruik verschillende functies in de verschillende fases!
Pagina 7
Exceptions in PHP 5
Tot nu toe hebben we enkel eenvoudige voorbeelden van foutafhandeling in PHP bekeken. We hebben gezien hoe die() een script genadeloos om zeep helpt, hoe trigger_error() iets flexibeler is in het geven van een foutmelden en hoe we met set_error_handler() een eigen foutafhandeling functie kunnen gebruiken.
Met de komst van exceptions in PHP 5 hebben we een uiterst krachtige manier van foutafhandeling tot onze beschikking gekregen. Dit mechanisme biedt veel meer flexibiliteit, vooral bij het opvangen van fouten en het weergeven van gedetailleerde foutinformatie.
In de rest van deze tutorial zal ik het gebruik van exceptions in PHP behandelen. Ik zal beginnen met het gebruik van try-throw-catch combinaties om vervolgens verder te gaan met iets complexere voorbeelden zoals het gebruik van error types en het uitbreiden van de Exception klasse. Omdat het gebruik van exceptions berust op het principe van object georiënteerd programmereren, is het voor het vervolg van deze tutorial makkelijk als je daar enige kennis van hebt.
Werken met exceptions
Het eerste dat je in gedachten moet houden bij het werken met exceptions, is dat het geen vervanging is van de interne error handler van PHP. Zo worden alle parse errors en fatale fouten nog steeds als lelijke melding op het scherm gezet. Maar deze fouten zou je in principe alleen bij het debuggen tegenkomen en de eindgebruiker zou ze nooit zien. De fouten die de eindgebruiker ziet zijn namelijk degenen die jij met behulp van exceptions weergeeft.
Laten we kijken naar een eenvoudig voorbeeldje:
Dit script levert de volgende foutmelding op:
Zoals we zien is dit een PHP foutmelding van het level E_ERROR. Een fatale fout waarbij het script verder niet uitgevoerd wordt.
Deze foutmelding wordt veroorzaakt omdat we de exception die we 'gegooid' hebben naar PHP niet netjes afgevangen hebben. Het afvangen van exceptions doe je met een try-catch combinatie.
Zoals we zien is de output nu gelijk aan de foutmelding die we gegeven hebben:
Het gooien van een exception kunnen we op elk willekeurig moment in een script doen, zolang we die exception maar netjes afvangen met een 'catch' blok. We hoeven dus ook niet per se een functie te gebruiken om een exception te kunnen gooien:
Ook hier krijgen we weer netjes de foutmelding:
Zoals je ziet wordt nadat er een exception gegooid is, de rest van het try block niet meer uitgevoerd. In bovenstaand voorbeeld is het dus ook niet nodig om de echo in een else statement te zetten. Verander de waarde van $getal maar eens naar bijvoorbeeld 6 en bekijk de output van het script.
Een andere manier om die lelijke foutmelding te voorkomen, is het gebruik van een eigen exception handler. Net als met set_error_handler() kunnen we nu de functie set_exception_handler() gebruiken:
De output is nu als volgt:
Het grote voordeel van de try-catch combinatie is echter dat je zelf kunt bepalen wanneer je een fout wilt afhandelen. Als een exception gegooid wordt en hij wordt nergens afgevangen, dan wordt direct de exception handler functie uitgevoerd en de rest van het script onderbroken.
Als je echter netjes met een catch blok de exception afvangt, loopt het script gewoon netjes door. Het afvangen van de fout zou bij wijze van spreken pas honderden regels code later gebeuren, zonder dat de rest van het script daar hinder van ondervindt.
Met de komst van exceptions in PHP 5 hebben we een uiterst krachtige manier van foutafhandeling tot onze beschikking gekregen. Dit mechanisme biedt veel meer flexibiliteit, vooral bij het opvangen van fouten en het weergeven van gedetailleerde foutinformatie.
In de rest van deze tutorial zal ik het gebruik van exceptions in PHP behandelen. Ik zal beginnen met het gebruik van try-throw-catch combinaties om vervolgens verder te gaan met iets complexere voorbeelden zoals het gebruik van error types en het uitbreiden van de Exception klasse. Omdat het gebruik van exceptions berust op het principe van object georiënteerd programmereren, is het voor het vervolg van deze tutorial makkelijk als je daar enige kennis van hebt.
Werken met exceptions
Het eerste dat je in gedachten moet houden bij het werken met exceptions, is dat het geen vervanging is van de interne error handler van PHP. Zo worden alle parse errors en fatale fouten nog steeds als lelijke melding op het scherm gezet. Maar deze fouten zou je in principe alleen bij het debuggen tegenkomen en de eindgebruiker zou ze nooit zien. De fouten die de eindgebruiker ziet zijn namelijk degenen die jij met behulp van exceptions weergeeft.
Laten we kijken naar een eenvoudig voorbeeldje:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
function groter_dan_5($int)
{
if($int <= 5)
{
throw new Exception('FOUT: Het getal '.$int.' is niet groter dan 5');
}
return true;
}
groter_dan_5(4);
?>
Dit script levert de volgende foutmelding op:
Fatal error: Uncaught exception 'Exception' with message 'FOUT: Het getal 4 is niet groter dan 5' in C:\wamp\www\test.php:9
Stack trace: #0 C:\wamp\www\test.php(14): groter_dan_5(4) #1 {main} thrown in C:\wamp\www\test.php on line 9
Zoals we zien is dit een PHP foutmelding van het level E_ERROR. Een fatale fout waarbij het script verder niet uitgevoerd wordt.
Deze foutmelding wordt veroorzaakt omdat we de exception die we 'gegooid' hebben naar PHP niet netjes afgevangen hebben. Het afvangen van exceptions doe je met een try-catch combinatie.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
function groter_dan_5($int)
{
if($int <= 5)
{
throw new Exception('FOUT: Het getal '.$int.' is niet groter dan 5');
}
return true;
}
try
{
groter_dan_5(4);
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>
Zoals we zien is de output nu gelijk aan de foutmelding die we gegeven hebben:
FOUT: Het getal 4 is niet groter dan 5
Het gooien van een exception kunnen we op elk willekeurig moment in een script doen, zolang we die exception maar netjes afvangen met een 'catch' blok. We hoeven dus ook niet per se een functie te gebruiken om een exception te kunnen gooien:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
$getal = 4;
try
{
if($getal <= 5)
{
throw new Exception('FOUT: Het getal '.$getal.' is niet groter dan 5');
}
echo 'Groter dan 5!';
}
catch(Exception $e)
{
echo $e->getMessage();
}
?>
Ook hier krijgen we weer netjes de foutmelding:
FOUT: Het getal 4 is niet groter dan 5
Zoals je ziet wordt nadat er een exception gegooid is, de rest van het try block niet meer uitgevoerd. In bovenstaand voorbeeld is het dus ook niet nodig om de echo in een else statement te zetten. Verander de waarde van $getal maar eens naar bijvoorbeeld 6 en bekijk de output van het script.
Een andere manier om die lelijke foutmelding te voorkomen, is het gebruik van een eigen exception handler. Net als met set_error_handler() kunnen we nu de functie set_exception_handler() gebruiken:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
function myExceptionHandler($e)
{
echo 'FOUT: Er is een exception niet opgevangen.<br />';
echo 'Melding: '.$e->getMessage();
}
set_exception_handler('myExceptionHandler');
$getal = 4;
if($getal <= 5)
{
throw new Exception('FOUT: Het getal '.$getal.' is niet groter dan 5');
}
echo 'Groter dan 5!';
?>
De output is nu als volgt:
FOUT: Er is een exception niet opgevangen.
Melding: FOUT: Het getal 4 is niet groter dan 5
Het grote voordeel van de try-catch combinatie is echter dat je zelf kunt bepalen wanneer je een fout wilt afhandelen. Als een exception gegooid wordt en hij wordt nergens afgevangen, dan wordt direct de exception handler functie uitgevoerd en de rest van het script onderbroken.
Als je echter netjes met een catch blok de exception afvangt, loopt het script gewoon netjes door. Het afvangen van de fout zou bij wijze van spreken pas honderden regels code later gebeuren, zonder dat de rest van het script daar hinder van ondervindt.
Pagina 8
Gedetailleerde foutinformatie verkrijgen
Zoals ik al eerder zei, kunnen we met exceptions zeer eenvoudig gedetailleerde informatie geven over de fout die is opgetreden. De Exception klasse biedt daarom naast de methode getMessage(), die we al eerder zagen, nog enkele andere methoden die meer kunnen vertellen.
De methoden die de Exception klasse ter beschikking heeft zijn de volgende:
[li]getMessage() - De foutmelding waarmee de exception aangemaakt is[/li]
[li]getCode() - De code die aan de exception is meegegeven[/li]
[li]getFile() - Het bestand waarin de exception gegooid is[/li]
[li]getLine() - Het regelnummer waarop de exception gegooid is[/li]
[li]getTrace() - Een array met daarin de backtrace[/li]
[li]getTraceAsString() - De backtrace als string[/li]
Laten we weer eens kijken naar een voorbeeld:
We zien dat alleen het catch blok veranderd is en dat er meer informatie weergegeven wordt. De output ziet er nu dan ook als volgt uit:
Uiteraard kun je in een catch blok elke manier van foutafhandeling toepassen die je maar kunt bedenken. Dus zoals in dit voorbeeld het tonen op het scherm, maar ook de fout mailen naar een emailadres of opslaan in een database behoort tot de mogelijkheden. Met de standaard exception klasse heb je bovenstaande methoden tot je beschikking om een gedetailleerde foutmelding aan te maken.
Waar het gebruik van exceptions voornamelijk tot zijn recht komt is binnen het object georiënteerd programmeren. Je kunt je niet veroorloven om vanuit een klasse output te gaan geven, vandaar dat je een exception gebruikt die je afvangt bij bijvoorbeeld het instantiëren van die klasse.
Dit voorbeeld laat zien wat er gebeurt als we een instantie aan proberen te maken van de klasse 'Gebruiker' maar geen geldige tweede parameter meegeven (de leeftijd moet een integer zijn). De output is de volgende:
Op deze manier kunnen we de fouten die binnen een klasse optreden afvangen en een nette foutmelding genereren.
De methoden die de Exception klasse ter beschikking heeft zijn de volgende:
[li]getMessage() - De foutmelding waarmee de exception aangemaakt is[/li]
[li]getCode() - De code die aan de exception is meegegeven[/li]
[li]getFile() - Het bestand waarin de exception gegooid is[/li]
[li]getLine() - Het regelnummer waarop de exception gegooid is[/li]
[li]getTrace() - Een array met daarin de backtrace[/li]
[li]getTraceAsString() - De backtrace als string[/li]
Laten we weer eens kijken naar een voorbeeld:
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
$getal = 4;
try
{
if($getal <= 5)
{
throw new Exception('Het getal '.$getal.' is niet groter dan 5');
}
echo 'Groter dan 5!';
}
catch(Exception $e)
{
echo 'Error: '.$e->getMessage().'<br />';
echo 'Code: '.$e->getCode().'<br />';
echo 'File: '.$e->getFile().'<br />';
echo 'Line: '.$e->getLine().'<br />';
}
?>
We zien dat alleen het catch blok veranderd is en dat er meer informatie weergegeven wordt. De output ziet er nu dan ook als volgt uit:
Error: Het getal 4 is niet groter dan 5
Code: 0
File: C:\wamp\www\test.php
Line: 10
Uiteraard kun je in een catch blok elke manier van foutafhandeling toepassen die je maar kunt bedenken. Dus zoals in dit voorbeeld het tonen op het scherm, maar ook de fout mailen naar een emailadres of opslaan in een database behoort tot de mogelijkheden. Met de standaard exception klasse heb je bovenstaande methoden tot je beschikking om een gedetailleerde foutmelding aan te maken.
Waar het gebruik van exceptions voornamelijk tot zijn recht komt is binnen het object georiënteerd programmeren. Je kunt je niet veroorloven om vanuit een klasse output te gaan geven, vandaar dat je een exception gebruikt die je afvangt bij bijvoorbeeld het instantiëren van die klasse.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
class Gebruiker
{
protected $sGebruikersnaam;
protected $iLeeftijd;
public function __construct($sGebruikersnaam, $iLeeftijd)
{
$this->sGebruikersnaam = $sGebruikersnaam;
if(is_numeric($iLeeftijd))
{
$this->iLeeftijd = $iLeeftijd;
}
else
{
throw new Exception('Aanmaken instantie van "'.__CLASS__.'" mislukt: Geen geldige waarde voor parameter leeftijd.');
}
}
}
try
{
$oGebruiker = new Gebruiker('Henk', 'test');
}
catch(Exception $e)
{
echo 'Error: '.$e->getMessage().'<br />';
echo 'Code: '.$e->getCode().'<br />';
echo 'File: '.$e->getFile().'<br />';
echo 'Line: '.$e->getLine().'<br />';
}
?>
Dit voorbeeld laat zien wat er gebeurt als we een instantie aan proberen te maken van de klasse 'Gebruiker' maar geen geldige tweede parameter meegeven (de leeftijd moet een integer zijn). De output is de volgende:
Error: Aanmaken instantie van "Gebruiker" mislukt: Geen geldige waarde voor parameter leeftijd.
Code: 0
File: C:\wamp\www\test.php
Line: 19
Op deze manier kunnen we de fouten die binnen een klasse optreden afvangen en een nette foutmelding genereren.
Pagina 9
Uitgebreide foutafhandeling met gebruik van foutco
Bij de exceptions die we nu behandeld hebben, gaven we bij het gooien ervan een nette foutmelding mee. Echter kunnen we dit nog verder uitbreiden door naast een foutmelding ook een foutcode mee te geven. In het catch blok kunnen vervolgens aan de hand van deze foutcode verschillende acties uitvoeren.
De output van dit script is als volgt:
In dit script wordt er bij het gooien van een exception ook een foutcode als tweede parameter meegegeven. Vervolgens kunnen we in het catch blok met de getCode() methode deze foutcode ophalen en bepalen welke actie uitgevoerd moet worden.
In het voorbeeld zit de fout in het feit dat de gekozen avatar niet bestaat. De exception krijgt in dat geval de foutcode 4 (via de constante AVATAR_ERROR) mee. In het catchblok hangen we vervolgens een foutmelding aan deze code en stoppen we tevens de uitvoer van het verdere script.
Normaal gesproken zou je het bepalen van de foutmelding niet op deze manier op willen lossen. Je wilt namelijk dat de foutmelding direct al vanuit de klasse komt zodat je deze met $e->getMessage() kunt ophalen. Ik heb het nu alleen ter voorbeeld in het catch blok opgenomen om te illustreren hoe je met verschillende foutcodes verschillende acties zou kunnen uitvoeren.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
class Gebruiker
{
const NAME_ERROR = 1;
const AGE_ERROR = 2;
const AVATAR_ERROR = 4;
protected $sGebruikersnaam;
protected $iLeeftijd;
protected $sAvatar;
protected $sDir = 'avatars/';
function __construct($sGebruikersnaam, $iLeeftijd, $sAvatar)
{
if(strlen($sGebruikersnaam) < 3)
{
throw new Exception('Aanmaken instantie van "'.__CLASS__.'" mislukt.', self::NAME_ERROR);
}
if(!is_numeric($iLeeftijd))
{
throw new Exception('Aanmaken instantie van "'.__CLASS__.'" mislukt.', self::AGE_ERROR);
}
if(!file_exists($this->sDir.$sAvatar))
{
throw new Exception('Aanmaken instantie van "'.__CLASS__.'" mislukt.', self::AVATAR_ERROR);
}
$this->sGebruikersnaam = $sGebruikersnaam;
$this->iLeeftijd = $iLeeftijd;
$this->sAvatar = $sAvatar;
}
}
try
{
$gebruiker = new Gebruiker('Henk', 25, 'plaatje.png');
}
catch(Exception $e)
{
echo 'Error: '.$e->getMessage().'<br />';
echo 'File: '.$e->getFile().'<br />';
echo 'Line: '.$e->getLine().'<br />';
if($e->getCode() == 1)
{
echo 'Details: De minimale lengte van een gebruikersnaam is 3 tekens.<br />';
}
elseif($e->getCode() == 2)
{
echo 'Details: De opgegeven leeftijd is geen integer.<br />';
}
elseif($e->getCode() == 4)
{
echo 'Details: Het opgegeven bestand bestaat niet. De uitvoer van het script wordt gestopt...';
exit();
}
}
?>
De output van dit script is als volgt:
Error: Aanmaken instantie van "Gebruiker" mislukt.
File: C:\wamp\www\test.php
Line: 28
Details: Het opgegeven bestand bestaat niet. De uitvoer van het script wordt gestopt...
In dit script wordt er bij het gooien van een exception ook een foutcode als tweede parameter meegegeven. Vervolgens kunnen we in het catch blok met de getCode() methode deze foutcode ophalen en bepalen welke actie uitgevoerd moet worden.
In het voorbeeld zit de fout in het feit dat de gekozen avatar niet bestaat. De exception krijgt in dat geval de foutcode 4 (via de constante AVATAR_ERROR) mee. In het catchblok hangen we vervolgens een foutmelding aan deze code en stoppen we tevens de uitvoer van het verdere script.
Normaal gesproken zou je het bepalen van de foutmelding niet op deze manier op willen lossen. Je wilt namelijk dat de foutmelding direct al vanuit de klasse komt zodat je deze met $e->getMessage() kunt ophalen. Ik heb het nu alleen ter voorbeeld in het catch blok opgenomen om te illustreren hoe je met verschillende foutcodes verschillende acties zou kunnen uitvoeren.
Pagina 10
Uitbreiden van de standaard Exception klasse
Er zijn situaties waarbij de functionaliteit van de standaard Exception klasse niet voldoende is. In deze gevallen is het het makkelijkst om de standaard klasse eenvoudigweg uit te breiden. Dit kunnen we doen door een eigen exeception klasse te schrijven die de standaard klasse 'extend'.
De klasse MyException is een voorbeeld van een zelf geschreven exception klasse. Aangezien dit een uitbreiding is van de standaard klasse, hebben we de beschikking over alle members en methoden uit die standaard klasse.
In dit voorbeeld gebruiken we de methode getError() om een zelf geschreven error weer te geven waarin verschillende methoden uit de standaard klasse verwerkt zijn. Ook zien we dat we nu geen 'new Exception' gooien, maar een 'new MyException'. Ook bij het afvangen van de exception gebruiken we MyException.
De output van dit voorbeeld is:
Nu is het ook mogelijk om meerdere zelf geschreven exception klasses te gebruiken voor verschillende fouten. Deze klasses kunnen een uitbreiding van de Exception klasse zijn of van elkaar.
Wederom gaat het in bovenstaande code fout omdat de gekozen avatar niet bestaat. Nu wordt er echter een AvatarException gegooid als dat gebeurt. In alle andere gevallen wordt een MyException gebruikt.
Omdat er nu twee mogelijke exceptions zich voor kunnen doen, moeten we ook zorgen dat we beide soorten afvangen. We hebben nu dus twee catck blokken nodig om dit af te handelen. In bovenstaand voorbeeld wordt natuurlijk het catch blok, waarin de AvatarException afgevangen wordt, uitgevoerd.
De output is dan ook als volgt:
We zien dat we, ondanks dat we een zelf geschreven klasse gebruiken, nog steeds de beschikking hebben over de methoden getFile() en getLine() uit de standaard klasse. Dat is de mooie eigenschap van het extenden van klasses.
Voorbeeld van PDO foutafhandeling
Eerder in deze tutorial heb ik de foutafhanding bij een sql query behandeld. Daar gebruikten we trigger_error() om een fout van MySQL op te halen en weer te geven.
Tegenwoordig wordt steeds vaker gebruik gemaakt van PDO om de connectie met een database te beheren. Een voorbeeld van hoe je de foutafhandeling bij PDO zou kunnen toepassen:
We zien hier dat PDO ook gebruik maakt van een eigen exception klasse, namelijk PDOException. Deze klasse wordt gebruikt om fouten die zich voordoen tijdens het gebruik van de PDO klasse te verwerken.
Een voorbeeld van de output zou deze kunnen zijn:
Meer over PDO kun je in deze tutorial vinden.
Foutafhandeling bij de Swift mailer klasse
Een ander voorbeeld waar gebruik gemaakt wordt van custom exception klasses, is bij het gebruik van Swift mailer.
Duidelijk is ook hier weer het verschil tussen de twee exception klasses die gebruikt worden. Waar Swift_ConnectionException gebruikt wordt om een fout in de verbinding met de server af te handelen, zal er een Swift_Message_MimeException gegooid worden als het aanmaken van het mailtje niet lukt.
Zoals je ziet biedt het gebruik van exceptions je heel veel ruimte om je foutafhandeling zo toe te passen als jij dat zelf wilt. Hoe complex je de foutafhandeling maakt hangt natuurlijk af van het soort applicatie dat je maakt en van de methode waarop jij gebruikers in aanraking wilt laten komen met foutmeldingen.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
class MyException extends Exception
{
public function getError()
{
$sMessage = 'Er is een fout opgetreden in '.$this->getFile().' op regel '.$this->getLine().'<br />';
$sMessage .= 'Foutmelding: <i>'.$this->getMessage().'</i><br />';
return $sMessage;
}
}
class Gebruiker
{
protected $sGebruikersnaam;
protected $iLeeftijd;
protected $sAvatar;
protected $sDir = 'avatars/';
function __construct($sGebruikersnaam, $iLeeftijd, $sAvatar)
{
if(strlen($sGebruikersnaam) < 3)
{
throw new MyException('Aanmaken instantie van "'.__CLASS__.'" mislukt: Gebruikersnaam moet minimaal 3 tekens zijn.');
}
if(!is_numeric($iLeeftijd))
{
throw new MyException('Aanmaken instantie van "'.__CLASS__.'" mislukt: Geen geldige leeftijd ingevuld');
}
if(!file_exists($this->sDir.$sAvatar))
{
throw new MyException('Aanmaken instantie van "'.__CLASS__.'" mislukt: Gekozen avatar bestaat niet');
}
$this->sGebruikersnaam = $sGebruikersnaam;
$this->iLeeftijd = $iLeeftijd;
$this->sAvatar = $sAvatar;
}
}
try
{
$gebruiker = new Gebruiker('Henk', 25, 'plaatje.png');
}
catch(MyException $e)
{
echo $e->getError();
}
?>
De klasse MyException is een voorbeeld van een zelf geschreven exception klasse. Aangezien dit een uitbreiding is van de standaard klasse, hebben we de beschikking over alle members en methoden uit die standaard klasse.
In dit voorbeeld gebruiken we de methode getError() om een zelf geschreven error weer te geven waarin verschillende methoden uit de standaard klasse verwerkt zijn. Ook zien we dat we nu geen 'new Exception' gooien, maar een 'new MyException'. Ook bij het afvangen van de exception gebruiken we MyException.
De output van dit voorbeeld is:
Er is een fout opgetreden in C:\wamp\www\test.php op regel 34
Foutmelding: Aanmaken instantie van "Gebruiker" mislukt: Gekozen avatar bestaat niet
Nu is het ook mogelijk om meerdere zelf geschreven exception klasses te gebruiken voor verschillende fouten. Deze klasses kunnen een uitbreiding van de Exception klasse zijn of van elkaar.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
class MyException extends Exception
{
public function getError()
{
$sMessage = 'Er is een fout opgetreden in '.$this->getFile().' op regel '.$this->getLine().'<br />';
$sMessage .= 'Foutmelding: <i>'.$this->getMessage().'</i><br />';
return $sMessage;
}
}
class AvatarException extends Exception
{
public function avatarError()
{
$sMessage = 'Er is een fout opgetreden bij het laden van de avatar: "'.$this->getMessage().'"<br />';
return $sMessage;
}
}
class Gebruiker
{
protected $sGebruikersnaam;
protected $iLeeftijd;
protected $sAvatar;
protected $sDir = 'avatars/';
function __construct($sGebruikersnaam, $iLeeftijd, $sAvatar)
{
if(strlen($sGebruikersnaam) < 3)
{
throw new MyException('Aanmaken instantie van "'.__CLASS__.'" mislukt: Gebruikersnaam moet minimaal 3 tekens zijn.');
}
if(!is_numeric($iLeeftijd))
{
throw new MyException('Aanmaken instantie van "'.__CLASS__.'" mislukt: Geen geldige leeftijd ingevuld');
}
if(!file_exists($this->sDir.$sAvatar))
{
throw new AvatarException('Gekozen avatar bestaat niet');
}
$this->sGebruikersnaam = $sGebruikersnaam;
$this->iLeeftijd = $iLeeftijd;
$this->sAvatar = $sAvatar;
}
}
try
{
$gebruiker = new Gebruiker('Henk', 25, 'plaatje.png');
}
catch(MyException $e)
{
echo $e->getError();
}
catch(AvatarException $e)
{
echo $e->avatarError();
echo 'Bestand: '.$e->getFile().'<br />';
echo 'Regel: '.$e->getLine().'<br />';
echo 'Uitvoer van het script wordt gestopt...';
exit();
}
?>
Wederom gaat het in bovenstaande code fout omdat de gekozen avatar niet bestaat. Nu wordt er echter een AvatarException gegooid als dat gebeurt. In alle andere gevallen wordt een MyException gebruikt.
Omdat er nu twee mogelijke exceptions zich voor kunnen doen, moeten we ook zorgen dat we beide soorten afvangen. We hebben nu dus twee catck blokken nodig om dit af te handelen. In bovenstaand voorbeeld wordt natuurlijk het catch blok, waarin de AvatarException afgevangen wordt, uitgevoerd.
De output is dan ook als volgt:
Er is een fout opgetreden bij het laden van de avatar: "Gekozen avatar bestaat niet"
Bestand: C:\wamp\www\test.php
Regel: 42
Uitvoer van het script wordt gestopt...
We zien dat we, ondanks dat we een zelf geschreven klasse gebruiken, nog steeds de beschikking hebben over de methoden getFile() en getLine() uit de standaard klasse. Dat is de mooie eigenschap van het extenden van klasses.
Voorbeeld van PDO foutafhandeling
Eerder in deze tutorial heb ik de foutafhanding bij een sql query behandeld. Daar gebruikten we trigger_error() om een fout van MySQL op te halen en weer te geven.
Tegenwoordig wordt steeds vaker gebruik gemaakt van PDO om de connectie met een database te beheren. Een voorbeeld van hoe je de foutafhandeling bij PDO zou kunnen toepassen:
<?php
try
{
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$sql = "SELECT naam FROM bestaat_niet";
$results = $db->query($sql);
foreach($results as $row)
{
echo $row['naam'].'<br>';
}
}
catch(PDOException $e)
{
echo '<pre>';
echo 'Regelnummer: '.$e->getLine().'<br>';
echo 'Bestand: '.$e->getFile().'<br>';
echo 'Foutmelding: '.$e->getMessage().'<br>';
echo '</pre>';
}
?>
We zien hier dat PDO ook gebruik maakt van een eigen exception klasse, namelijk PDOException. Deze klasse wordt gebruikt om fouten die zich voordoen tijdens het gebruik van de PDO klasse te verwerken.
Een voorbeeld van de output zou deze kunnen zijn:
Regelnummer: 8
Bestand: /Users/jorendewit/Sites/jorendewit/phphulp/pdo.php
Foutmelding: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.bestaat_niet' doesn't exist
Meer over PDO kun je in deze tutorial vinden.
Foutafhandeling bij de Swift mailer klasse
Een ander voorbeeld waar gebruik gemaakt wordt van custom exception klasses, is bij het gebruik van Swift mailer.
<?php
ini_set('display_errors', 'On');
error_reporting(E_ALL);
try
{
$swift = new Swift(new Swift_Connection_SMTP('smtp.server.nl'));
$message = new Swift_Message('Onderwerp', 'Body');
$swift->send($message, '[email protected]', '[email protected]');
echo 'Bericht verstuurd';
}
catch (Swift_ConnectionException $e)
{
echo 'Er is een foutopgetreden bij het verbinden met de SMTP server: ' . $e->getMessage();
}
catch (Swift_Message_MimeException $e)
{
echo 'Er is een fout opgetreden bij het opbouwen van het mailtje: ' . $e->getMessage();
}
?>
Duidelijk is ook hier weer het verschil tussen de twee exception klasses die gebruikt worden. Waar Swift_ConnectionException gebruikt wordt om een fout in de verbinding met de server af te handelen, zal er een Swift_Message_MimeException gegooid worden als het aanmaken van het mailtje niet lukt.
Zoals je ziet biedt het gebruik van exceptions je heel veel ruimte om je foutafhandeling zo toe te passen als jij dat zelf wilt. Hoe complex je de foutafhandeling maakt hangt natuurlijk af van het soort applicatie dat je maakt en van de methode waarop jij gebruikers in aanraking wilt laten komen met foutmeldingen.
Pagina 11
Slotwoord en referenties
In deze tutorial heb ik geprobeerd een inzicht te geven in de mogelijkheden die je binnen PHP hebt als het gaat om foutafhandeling. Een feit dat als rode draad door deze tutorial loopt, en ik kan het niet vaak genoeg zeggen, is dat een goede foutafhandeling de basis voor ieder goed script is.
We zijn begonnen met de meest basale vormen van foutafhandeling. Functies als die() en trigger_error() zijn aan de orde gekomen en we hebben gezien hoe deze functies toegepast kunnen worden. Ook hebben we gekeken naar set_error_handler() om zelf geschreven foutafhandeling functies te kunnen gebruiken in plaats van de standaard error handler van PHP.
Ook hebben we een klein uitstapje gemaakt en de link gelegd tussen controle van gebruikersinput en foutafhandeling in een script. Ik kan niet sterk genoeg benadrukken dat, in het kader van veiligheid, input van gebruikers altijd gecontroleerd moet worden en dat een degelijke foutafhandeling in plaats moet zijn.
Tenslotte hebben we gekeken naar het gebruik van exceptions in PHP 5. Deze methode van foutafhandeling biedt de meeste mogelijkheden en is het meest flexibel van allemaal. Een belangrijk punt dat we echter niet moeten vergeten is dat parse errors en fatale fouten niet met een exception afgevangen kunnen worden. Dus een combinatie tussen een eigen error handler om de standaard PHP fouten af te vangen en het gebruik van exceptions om de gebruikers van nette foutmeldingen te voorzien, zou een ideale situatie zijn.
Tot slot wil ik nogmaals benadrukken dat de keuze van het soort foutafhandeling afhankelijk is van de fase waarin de website zich op dat moment bevindt. In de ontwikkelfase wil je tijdens het debuggen de foutmeldingen zo gedetailleerd mogelijk op het scherm hebben. In de productiefase daarentegen, waarin de site voor het publiek toegankelijk is, wil je een nette foutmelding op het scherm tonen en de gedetaileerde foutmelding loggen of mailen. Pas de foutafhandeling daarop aan en je zult altijd op een juiste manier op de hoogte blijven van fouten.
Opmerkingen en suggesties over deze tutorial zijn natuurlijk altijd welkom!
Deze tutorial is ook hier te vinden.
Bronvermelding
[li]W3Schools: PHP Error Handling[/li]
[li]W3Schools: PHP Exception Handling[/li]
[li]Error Handling in PHP: Coding Devensively[/li]
[li]Error Handling in PHP: Introducing Exceptions in PHP 5 [/li]
We zijn begonnen met de meest basale vormen van foutafhandeling. Functies als die() en trigger_error() zijn aan de orde gekomen en we hebben gezien hoe deze functies toegepast kunnen worden. Ook hebben we gekeken naar set_error_handler() om zelf geschreven foutafhandeling functies te kunnen gebruiken in plaats van de standaard error handler van PHP.
Ook hebben we een klein uitstapje gemaakt en de link gelegd tussen controle van gebruikersinput en foutafhandeling in een script. Ik kan niet sterk genoeg benadrukken dat, in het kader van veiligheid, input van gebruikers altijd gecontroleerd moet worden en dat een degelijke foutafhandeling in plaats moet zijn.
Tenslotte hebben we gekeken naar het gebruik van exceptions in PHP 5. Deze methode van foutafhandeling biedt de meeste mogelijkheden en is het meest flexibel van allemaal. Een belangrijk punt dat we echter niet moeten vergeten is dat parse errors en fatale fouten niet met een exception afgevangen kunnen worden. Dus een combinatie tussen een eigen error handler om de standaard PHP fouten af te vangen en het gebruik van exceptions om de gebruikers van nette foutmeldingen te voorzien, zou een ideale situatie zijn.
Tot slot wil ik nogmaals benadrukken dat de keuze van het soort foutafhandeling afhankelijk is van de fase waarin de website zich op dat moment bevindt. In de ontwikkelfase wil je tijdens het debuggen de foutmeldingen zo gedetailleerd mogelijk op het scherm hebben. In de productiefase daarentegen, waarin de site voor het publiek toegankelijk is, wil je een nette foutmelding op het scherm tonen en de gedetaileerde foutmelding loggen of mailen. Pas de foutafhandeling daarop aan en je zult altijd op een juiste manier op de hoogte blijven van fouten.
Opmerkingen en suggesties over deze tutorial zijn natuurlijk altijd welkom!
Deze tutorial is ook hier te vinden.
Bronvermelding
[li]W3Schools: PHP Error Handling[/li]
[li]W3Schools: PHP Exception Handling[/li]
[li]Error Handling in PHP: Coding Devensively[/li]
[li]Error Handling in PHP: Introducing Exceptions in PHP 5 [/li]
Reacties
0