Skip to content

7. Uitzonderingen en fouten

Wanneer een methode van een klasse een onherstelbare fout tegenkomt (bestand bestaat niet, geen verbinding met de database, netwerkverbinding buiten gebruik), geeft deze geen foutmelding weer op een console (bestand, database), maar genereert een uitzondering. Alle uitzonderingen zijn afgeleid van de klasse [\Exception]. Naast uitzonderingen genereert de interne werking van PHP ook fouten waarvan de basisklasse de klasse [\Error] is. Beide klassen implementeren de interface PHP [\Throwable].

7.1. De scriptboomstructuur

Image

7.2. De interface [\Throwable]

De interface [\Throwable] is als volgt:

Image

De functies van de methoden van de interface zijn als volgt:

Image

7.3. De vooraf gedefinieerde uitzonderingen in PHP 7

PHP 7 definiëren verschillende klassen van uitzonderingen:

Image

  • in [1], de vooraf gedefinieerde uitzonderingen in PHP;
  • in [2], de uitzonderingen van de bibliotheek SPL (Standaardbibliotheek PHP) van PHP 7. De bibliotheek SPL is een verzameling klassen en interfaces die bedoeld zijn om problemen op te lossen waarmee ontwikkelaars vaak worden geconfronteerd.

7.4. De vooraf gedefinieerde fouten in PHP 7

PHP 7 definieert verschillende foutklassen:

Image

De klasse [\Error] is de bovenliggende klasse van alle vooraf gedefinieerde fouten in PHP. Met de klasse [ErrorException] kan een instantie van de klasse [\Error] worden ingekapseld in een instantie van de klasse [\Exception]. Hierdoor kan het foutbeheer worden gestandaardiseerd door uitsluitend uitzonderingen te verwerken.

7.5. Voorbeeld 1

Het eerste voorbeeld [exceptions-01.php] toont zowel fouten van het type PHP als een uitzondering:


<?php

// weergave van alle fouten
ini_set("error_reporting", E_ALL);
ini_set("display_errors", "on");
// code --------
$var=[];
// onbekende sleutel
print $var["abcd"];
// delen door nul
$var=7/0;
var_dump($var);
// tabel met vaste grenzen
$array = new \SplFixedArray(5);
$array[1] = 2;
$array[4] = "foo";
// index buiten de grenzen
$array[5]=8;

Opmerkingen

  • regel 4: PHP wordt gevraagd om alle fouten te melden. De tweede parameter is het gewenste foutniveau:

Image

Image

  • regel 5: er wordt gevraagd om de fouten op de console weer te geven;
  • regel 9: er wordt een beroep gedaan op een niet-bestaand element van de array [$var];
  • regel 11: er wordt gedeeld door nul;
  • regel 14: er wordt een instantie van de klasse [SplFixedArray] aangemaakt. Met deze klasse kan een array met vaste grenzen en gehele indices worden aangemaakt;
  • regel 18: er wordt een beroep gedaan op een niet-bestaand element van de array;

Resultaten

1
2
3
4
5
6
7
8
9
Notice: Undefined index: abcd in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-01.php on line 9

Warning: Division by zero in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-01.php on line 11
float(INF)

Fatal error: Uncaught RuntimeException: Index invalid or out of range in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-01.php:18
Stack trace:
#0 {main}
thrown in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-01.php on line 18

Opmerkingen

  • regel 1 van de resultaten: het benaderen van een niet-bestaande sleutel van een array veroorzaakt een fout PHP van niveau [E_NOTICE]. Dit onderbreekt de uitvoering van het script niet;
  • regel 3 van de resultaten: het delen van een getal door nul veroorzaakt een fout PHP van niveau [E_WARNING]. Dit onderbreekt de uitvoering van het script niet;
  • regels 6-9 van de resultaten: het benaderen van een niet-bestaande index van een array [SplFixedArray] veroorzaakt een uitzondering van het type [RuntimeException] en onderbreekt de uitvoering van het script;

7.6. Uitzonderingen afhandelen

Het script [exceptions-02.php] laat zien hoe uitzonderingen moeten worden afgehandeld:


<?php

// alle fouten worden weergegeven
ini_set("error_reporting", E_ALL);
ini_set("display_errors", "on");
// de code wordt omgeven door een try/catch
try {
  $var = [];
  // onbekende sleutel
  print $var["abcd"];
  // delen door nul
  $var = 7 / 0;
  var_dump($var);
  // array met vaste grenzen
  $array = new \SplFixedArray(5);
  $array[1] = 2;
  $array[4] = "foo";
  // index buiten het bereik
  $array[5] = 8;
  // controle
  print "ce message ne sera pas affiché\n";
} catch (\Throwable $ex) {
  // \Throwable is de interface die door de meeste fouten en uitzonderingen wordt geïmplementeerd
  // de uitzondering wordt weergegeven
  print "erreur, message : " . $ex->getMessage() . ", type : " . get_class($ex) . "\n";
}

Opmerkingen

  • dit is hetzelfde script als in de vorige paragraaf. Alleen is nu de code in de regels 8-19, die fouten kan veroorzaken, omgeven door een try/catch-structuur: als de code in de regels 8-21 een uitzondering of fout veroorzaakt (trigger), wordt deze afgehandeld door de catch-clausule in de regels 22-26;
  • regel 22: de parameter van de clausule [catch] is het type uitzondering of fout dat we willen afhandelen. Door als type [\Throwable] op te geven, wat een interface is, geven we aan dat we elke klasse-instantie willen afhandelen die de interface [\Throwable] implementeert. Aangezien alle fout- en uitzonderingsklassen deze interface implementeren, verwerkt de clausule [catch] hier elke fout/uitzondering die in een klasse is ingekapseld;
  • regel 19: de instructie die de fout veroorzaakt en de uitzondering genereert. Zodra er een uitzondering optreedt, wordt er doorgeschakeld naar de clausule [catch]. De code achter regel 19 wordt dus niet uitgevoerd;

Resultaten

1
2
3
4
5
Notice: Undefined index: abcd in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-02.php on line 10

Warning: Division by zero in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-02.php on line 12
float(INF)
erreur, message : Index invalid or out of range, type : RuntimeException

Opmerkingen bij de resultaten

  • regels 1 en 3: hier zien we de fouten op niveau [E_NOTICE] en [E_WARNING]. Deze fouten zijn geen uitzonderingen en worden daarom niet afgehandeld door de clausule [catch];
  • regel 5: hier staat de foutmelding vermeld in de clausule [catch]. Er is dus een uitzondering opgetreden die is afgeleid van [\Exception] of een fout die is afgeleid van [\Error]. We zien hier dat het om de klasse [\RuntimeException] gaat;

7.7. Parameters van de clausule [catch]

Laten we het volgende script [exceptions-03.php] eens bekijken:


<?php

// alle fouten worden weergegeven
ini_set("error_reporting", E_ALL);
ini_set("display_errors", "on");

// een array met vaste grenzen
$array = new \SplFixedArray(5);
try {
  // index buiten het bereik
  $array[5] = 8;
} catch (\Throwable $ex) {
  // foutmelding weergeven
  print "Erreur 1 : " . $ex->getMessage() . "\n";
}

try {
  // index buiten de grenzen
  $array[5] = 8;
} catch (\Exception $ex) {
  // weergave van foutmelding
  print "Erreur 2 : " . $ex->getMessage() . "\n";
}

try {
  // waarde buiten het bereik
  $array[5] = 8;
} catch (\RuntimeException $ex) {
  // foutmelding weergeven
  print "Erreur 3 : " . $ex->getMessage() . "\n";
}
try {
  // delen door 0
  intdiv(5, 0);
} catch (\Throwable $ex) {
  // foutmelding weergeven
  print "Erreur 4 : " . $ex->getMessage() . "\n";
}

try {
  // delen door 0
  intdiv(5, 0);
} catch (\DivisionByzeroError $ex) {
  // foutmelding weergeven
  print "Erreur 5 : " . $ex->getMessage() . "\n";
}

try {
  // delen door 0
  intdiv(5, 0);
} catch (\Error $ex) {
  // foutmelding weergeven
  print "Erreur 6 : " . $ex->getMessage() . "\n";
}

try {
  // delen door 0
  intdiv(5, 0);
} catch (\Exception $ex) {
  // foutmelding weergeven
  print "Erreur 6 : " . $ex->getMessage() . "\n";
}

Opmerkingen

  • regels 8-31: 3 verschillende manieren om de uitzondering af te handelen die wordt gegenereerd door het gebruik van een onjuiste index met de klasse [\SplFixedArray]. We hebben gezien dat deze fout een uitzondering van het type [RuntimeException] genereert;
    • regel 12: verwerkt een fout van het type [\Throwable]. Dit is geldig, aangezien het type [RuntimeException] is afgeleid van het type [\Exception], dat de interface [\Throwable] implementeert;
    • regel 20: verwerkt een fout van het type [\Exception]. Dit is geldig, aangezien het type [RuntimeException] is afgeleid van het type [\Exception];
    • regel 28: verwerkt een fout van het type [\RuntimeException]. Dit is de aanbevolen methode, aangezien dit het exacte type is van de gegenereerde uitzondering;
  • regels 32-62: 4 verschillende manieren om de uitzondering af te handelen die wordt gegenereerd door de functie [intdiv] wanneer hieraan een deler gelijk aan 0 wordt doorgegeven. De functie [ intdiv ( int $dividend , int $divisor ) : int] voert de gehele deling $dividend / $divisor uit. Wanneer de deler nul is, wordt de uitzondering [\DivisionByzeroError] gegenereerd;
    • regel 35: elke fout die de interface [\Throwable] implementeert, wordt opgevangen. Dit is geldig;
    • regel 43: het exacte type van de fout wordt opgevangen: dit is de voorkeursmethode;
    • regel 51: het type [\Error] wordt opgevangen. Dit is geldig, aangezien de klasse [DivisionByzeroError] een uitbreiding is van de klasse [Error];
    • regel 59: het type [\Exception] wordt onderschept. Dit is ongeldig omdat de klasse [DivisionByzeroError] geen verband houdt met de klasse [\Exception];

Resultaten

Erreur 1 : Index invalid or out of range
Erreur 2 : Index invalid or out of range
Erreur 3 : Index invalid or out of range
Erreur 4 : Division by zero
Erreur 5 : Division by zero
Erreur 6 : Division by zero

Fatal error: Uncaught DivisionByZeroError: Division by zero in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-03.php:58
Stack trace:
#0 C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-03.php(58): intdiv(5, 0)
#1 {main}
thrown in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-03.php on line 58

7.8. Clausule [finally]

De try/catch-structuur kan een derde element bevatten en zo een try/catch/finally-structuur worden. De code van de clausule [finally] wordt in de volgende twee gevallen uitgevoerd:

  • de clausule [try] genereert geen uitzondering. Deze wordt dan volledig uitgevoerd, waarna de uitvoering van de code overgaat naar de clausule [finally], die volledig wordt uitgevoerd;
  • de clausule [try] genereert een uitzondering. Deze wordt dan uitgevoerd tot aan de instructie die de uitzondering genereert. De uitvoering van de code gaat vervolgens over naar de clausule [catch], die volledig wordt uitgevoerd. Vervolgens gaat de uitvoering van de code over naar de clausule [finally], die volledig wordt uitgevoerd;

Ten slotte wordt de code van de clausule [finally] nog steeds uitgevoerd. Dit scenario is nuttig in het volgende geval:

  • in de clausule [try] heeft de code resources (bestanden, databases, netwerkverbindingen, wachtrijen) verkregen. Over het algemeen zijn deze resources geheugenintensief. Ze moeten daarom zo snel mogelijk worden vrijgegeven (meestal wordt dit ‘sluiten’ genoemd);
  • als de resources zijn verkregen in [try], wordt het vrijgeven ervan in [finally] uitgevoerd. Dit zorgt ervoor dat in alle gevallen (fout of geen fout) de verkregen resources aan het systeem worden teruggegeven;

Het volgende script, [exemples/exceptions/exceptions-04.php], laat zien hoe de clausule [finally] in verschillende situaties werkt:


<?php

// of creëert een uitzonderingsexemplaar
$e = new \Exception("Erreur…");    
var_dump($e);

// eerste test
try {
  print "Premier test\n";
  throw $e;
} catch (\Exception $ex1) {
  print $ex1->getMessage() . "\n";
} finally {
  print "Terminé\n";
}

// tweede test
try {
  print "Second test\n";
} catch (\Exception $ex1) {
  print $ex1->getMessage() . "\n";
} finally {
  print "Terminé\n";
}

// derde test
try {
  print "Troisième test\n";
  return;
} catch (\Exception $ex1) {
  print $ex1->getMessage() . "\n";
} finally {
  print "Terminé\n";
}

Opmerkingen bij de code

  • regel 4: $e is een instantie van de vooraf gedefinieerde klasse [\Exception]. We zullen deze op verschillende plaatsen starten;
  • regels 8-15: de uitzondering $e wordt gegenereerd in [try] (regel 10);
  • regel 11: de uitzondering [\Exception] wordt opgevangen en de foutmelding ervan wordt naar de console geschreven;
  • regels 13-15: de clausule [finally] schrijft een bericht. Zoals eerder vermeld, zou dit bericht altijd moeten worden geschreven, ongeacht of er al dan niet een fout is in [try];
  • regels 18-24: er zit geen fout in de [try]. Ook hier zou men door moeten gaan naar de [finally];
  • regels 27-34: er staat een instructie [return] in de try-blok en er is geen fout. Men kan zich dan afvragen of er wordt doorgegaan naar de clausule [finally]. Uit de uitvoering blijkt dat dit inderdaad het geval is;

Resultaten

1
2
3
4
5
6
7
Premier test
Erreur…
Terminé
Second test
Terminé
Troisième test
Terminé

Laten we nog een ander geval bekijken, [exceptions-05.php]:


<?php

// vierde test
try {
  print "Quatrième test\n";
  exit;
} finally {
  print "Terminé\n";
}

Opmerkingen

  • regel 6: de instructie [exit] stopt de uitvoering van het script onmiddellijk: de clausule [finally] wordt niet uitgevoerd;
  • regels 4-9: een voorbeeld van try / catch / finally zonder de clausule [catch]. Dit is mogelijk;

Resultaten

Quatrième test

7.9. Eigen uitzonderingsklassen maken

In een wat groter project is het handig om onderscheid te maken tussen de verschillende fouten door ze in verschillende uitzonderingsklassen in te kapselen. In het vorige script hebben we gezien dat elke uitzondering kan worden opgevangen door een [catch (\Throwable]-clausule. Dit wordt aanbevolen als men geen idee heeft van de onderschepte fout en de afhandeling voor alle fouten hetzelfde is. Dit is soms het geval, maar vaak moet de afhandeling worden aangepast aan het exacte type fout. Dan is het nodig om onderscheid te maken tussen de fouten.

Laten we het volgende script [exceptions-06.php] eens bekijken:


<?php

// we definiëren onze eigen uitzonderingsfamilie
class Exception1 extends \RuntimeException {
  
}

class Exception2 extends \RuntimeException {
  
}

// of we gebruiken onze uitzonderingen
$e1 = new Exception1("Erreur1…");
var_dump($e1);
$e2 = new Exception2("Erreur2…");
var_dump($e2);

// eerste test
print ("premier test\n");
try {
  // we genereren een Exception1-type
  throw $e1;
} catch (Exception1 $ex1) {
  print "Exception 1" . "\n";
  print $ex1->getMessage() . "\n";
} catch (Exception2 $ex2) {
  print "Exception 2" . "\n";
  print $ex2->getMessage() . "\n";
}

// tweede test
print ("second test\n");
try {
  // we genereren een Exception2
  throw $e2;
} catch (Exception1 $ex1) {
  print "Exception 1" . "\n";
  print $ex1->getMessage() . "\n";
} catch (Exception2 $ex2) {
  print "Exception 2" . "\n";
  print $ex2->getMessage() . "\n";
}

// derde test
print ("troisième test\n");
try {
  // er wordt een Exception1-type gegenereerd
  throw $e1;
} catch (Exception1 | Exception2 $ex) {
  print "Exception 1 ou 2" . "\n";
  print $ex->getMessage() . "\n";
}

// vierde test
print ("quatrième test\n");
try {
  // er wordt een Exception2-type gegenereerd
  throw $e2;
} catch (Exception1 | Exception2 $ex) {
  print "Exception 1 ou 2" . "\n";
  print $ex->getMessage() . "\n";
}

Opmerkingen

  • regels 4-10: we definiëren twee klassen, [Exception1] en [Exception2], die beide zijn afgeleid van de vooraf gedefinieerde klasse [\RuntimeException]. De body van deze klassen is leeg. Met andere woorden, ze worden alleen gebruikt vanwege hun typen: juist omdat ze verschillende typen hebben, kunnen we deze twee uitzonderingen onderscheiden in de clausules [catch];
  • regels 13-16: we definiëren twee variabelen $e1 en $e2 met respectievelijk de typen [Exception1] en [Exception2];
  • regels 20-29: er is een try/catch/catch-structuur. Hiermee kunnen verschillende soorten uitzonderingen worden afgehandeld met verschillende clausules [catch];
  • regel 23: vangt uitzonderingen van het type [Exception1] op;
  • regel 26: vangt uitzonderingen van het type [Exception2] op;
  • regel 49: vangt uitzonderingen van het type [Exception1] of (|) [Exception2] op;

Resultaten

object(Exception1)#1 (7) {
  ["message":protected]=>
  string(10) "Erreur1…"
  ["string":"Exception":private]=>
  string(0) ""
  ["code":protected]=>
  int(0)
  ["file":protected]=>
  string(76) "C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-06.php"
  ["line":protected]=>
  int(13)
  ["trace":"Exception":private]=>
  array(0) {
  }
  ["previous":"Exception":private]=>
  NULL
}
object(Exception2)#2 (7) {
  ["message":protected]=>
  string(10) "Erreur2…"
  ["string":"Exception":private]=>
  string(0) ""
  ["code":protected]=>
  int(0)
  ["file":protected]=>
  string(76) "C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-06.php"
  ["line":protected]=>
  int(15)
  ["trace":"Exception":private]=>
  array(0) {
  }
  ["previous":"Exception":private]=>
  NULL
}
premier test
Exception 1
Erreur1…
second test
Exception 2
Erreur2…
troisième test
Exception 1 ou 2
Erreur1…
quatrième test
Exception 1 ou 2
Erreur2…

Toelichting bij de resultaten

  • regels 1-17: de ‘inhoud’ van een uitzondering:
    • regels 2-3: de foutmelding;
    • regels 6-7: de foutcode;
    • regels 8-9: de naam van het bestand waarin de uitzondering is opgetreden;
    • regels 10-11: de regel waarop de uitzondering zich heeft voorgedaan;
    • regels 15-16: de voorgaande uitzondering. Een uitzondering kan een andere uitzondering omvatten en zo een uitzonderingsstapel vormen. Met het attribuut [previous] kan deze stapel worden benut;

7.10. Een uitzondering opnieuw uitvoeren

Een uitzondering kan meerdere keren worden gegenereerd, zoals te zien is in het volgende script [exceptions-07.php]:


<?php

try {
  try {
    // er wordt een uitzondering gegenereerd
    throw new \Exception("test");
  } catch (\Exception $ex) {
    // de opgevangen uitzondering wordt opnieuw gegooid
    throw $ex;
  } finally {
    // we komen inderdaad in de finally terecht
    print "finally 1\n";
  }
} catch (\Exception $ex2) {
  // de oorspronkelijke uitzondering wordt correct opgevangen
  print $ex2->getMessage() . " dans try / catch / finally externe\n";
} finally {
  // we komen inderdaad in de finally terecht
  print "finally 2\n";
}

Opmerkingen

  • regel 6: er wordt een uitzondering gegenereerd;
  • regel 7: deze wordt opgevangen;
  • regel 9: de uitzondering wordt opnieuw gegenereerd. Deze wordt vervolgens doorgegeven aan de try/catch/finally-structuur van het bovenliggende niveau;
  • regel 14: deze wordt opnieuw opgevangen;
  • regels 10-12: de uitvoering laat zien dat zelfs na de [throw] van regel 9, we wel degelijk in de clausule [finally] van de try/catch/finally terechtkomen;

Resultaten

1
2
3
finally 1
test dans try / catch / finally externe
finally 2

7.11. Gebruik van een uitzonderingsstapel

Een uitzondering kan een andere uitzondering omvatten, die op haar beurt weer een andere uitzondering kan omvatten, waardoor uiteindelijk een stapel uitzonderingen ontstaat. Hier volgt een voorbeeld [exceptions-08.php]:

<?php

// we definiëren onze eigen uitzonderingsfamilie
class Exception1 extends \RuntimeException {

}

class Exception2 extends \RuntimeException {

}

class Exception3 extends \RuntimeException {

}

// of we gebruiken onze uitzonderingen
$e1 = new Exception1("Erreur 1…", 1, new Exception2("Erreur 2…", 2, new Exception3("Erreur 3…")));
var_dump($e1);
// de huidige uitzondering verwerken
print $e1->getMessage() . "\n";
$e = $e1;
while ($e->getPrevious() !== NULL) {
   // vorige uitzondering
  $e = $e->getPrevious();
   // foutmelding
  print $e->getMessage() . "\n";
}

Opmerkingen

  • regels 4-14: definiëren drie uitzonderingsklassen die zijn afgeleid van de vooraf gedefinieerde uitzondering [RuntimeException];
  • regel 17: een instantie van de klasse [Exception3] is ingekapseld in een instantie van de klasse [Exception2], die op haar beurt is ingekapseld in een instantie van de klasse [Exception1]. De constructor die hier wordt gebruikt, is de constructor van de klasse [Exception]:

Image

Met de derde parameter van de constructor kan een uitzondering worden ingekapseld. Dit kan nuttig zijn in het volgende scenario:

  • er wordt een methode M gedefinieerd die een uitzondering van het type [Exception1] kan genereren, en uitsluitend van dit type, bijvoorbeeld omwille van compatibiliteit met een interface;
  • maar in methode M kunnen ook andere soorten uitzonderingen optreden. Om een fout door te geven aan de code die methode M aanroept, zullen we deze uitzonderingen dan inkapselen in het type [Exception1], dat we vervolgens zullen afvuren. Zo gaat de informatie in de ingekapselde uitzondering – die de oorspronkelijke oorzaak van de fout was – niet verloren;
  • de regels 20-27 laten zien hoe de stapel van interne uitzonderingen binnen een uitzondering wordt beheerd;

Resultaten


object(Exception1)#1 (7) {
  ["message":protected]=>
  string(11) "Erreur 1…"
  ["string":"Exception":private]=>
  string(0) ""
  ["code":protected]=>
  int(1)
  ["file":protected]=>
  string(76) "C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-08.php"
  ["line":protected]=>
  int(17)
  ["trace":"Exception":private]=>
  array(0) {
  }
  ["previous":"Exception":private]=>
  object(Exception2)#2 (7) {
    ["message":protected]=>
    string(11) "Erreur 2…"
    ["string":"Exception":private]=>
    string(0) ""
    ["code":protected]=>
    int(2)
    ["file":protected]=>
    string(76) "C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-08.php"
    ["line":protected]=>
    int(17)
    ["trace":"Exception":private]=>
    array(0) {
    }
    ["previous":"Exception":private]=>
    object(Exception3)#3 (7) {
      ["message":protected]=>
      string(11) "Erreur 3…"
      ["string":"Exception":private]=>
      string(0) ""
      ["code":protected]=>
      int(0)
      ["file":protected]=>
      string(76) "C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-08.php"
      ["line":protected]=>
      int(17)
      ["trace":"Exception":private]=>
      array(0) {
      }
      ["previous":"Exception":private]=>
      NULL
    }
  }
}
Erreur 1…
Erreur 2…
Erreur 3…