Foutafhandeling in PHP (Error Handling)

  1. Inleiding
  2. Weergave en interpretatie van PHP fouten
  3. Basis foutafhandeling met die()
  4. Een flexibelere manier: trigger_error()
  5. Foutafhandeling en controle van variabelen
  6. Een eigen foutafhandeling functie met set_error_handler()
  7. Exceptions in PHP 5
  8. Gedetailleerde foutinformatie verkrijgen met exceptions
  9. Uitgebreide foutafhandeling met gebruik van foutcodes
  10. Uitbreiden van de standaard Exception klasse
  11. Slotwoord en referenties
  12. Reacties op deze tutorial

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'.
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?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:
Code
1
2
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.
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?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:
Code
1
2
3
4
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:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?php
try
{
    
$db = new PDO('mysql:host=localhost;dbname=test','user','password');
    
$db->setAttribute(PDO::ATTR_ERRMODEPDO::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:
Code
1
2
3
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.
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?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'ontvanger@e.mail''zender@e.mail');
    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.

Vorige Volgende