Skip to content

7. Ausnahmen und Fehler

Wenn eine Klassenmethode auf einen nicht behebbaren Fehler stößt (Datei existiert nicht, keine Verbindung zur Datenbank, Netzwerkverbindung unterbrochen), gibt sie keinen Fehler auf einer Konsole (Datei, Datenbank) aus, sondern löst eine Ausnahme aus. Alle Ausnahmen erben von der Klasse [\Exception]. Zusätzlich zu den Ausnahmen erzeugen auch die internen Operationen von PHP Fehler, deren Basisklasse die Klasse [\Error] ist. Beide Klassen implementieren die PHP-Schnittstelle [\Throwable].

7.1. Die Skript-Verzeichnisstruktur

Image

7.2. Die Schnittstelle [\Throwable]

Die Schnittstelle [\Throwable] sieht wie folgt aus:

Image

Die Methoden der Schnittstelle haben folgende Funktionen:

Image

7.3. Vordefinierte Ausnahmen in PHP 7

PHP 7 definiert mehrere Ausnahmeklassen:

Image

  • in [1] die vordefinierten Ausnahmen in PHP;
  • in [2] die Ausnahmen aus der SPL (Standard PHP Library) in PHP 7. Die SPL ist eine Sammlung von Klassen und Schnittstellen, die entwickelt wurden, um Probleme zu lösen, mit denen Entwickler häufig konfrontiert sind.

7.4. Vordefinierte Fehler in PHP 7

PHP 7 definiert mehrere Fehlerklassen:

Image

Die Klasse [\Error] ist die Oberklasse aller vordefinierten Fehler in PHP. Die Klasse [ErrorException] ermöglicht es Ihnen, eine Instanz der Klasse [\Error] in einer Instanz der Klasse [\Exception] zu kapseln. Dies ermöglicht eine standardisierte Fehlerbehandlung, indem ausschließlich mit Ausnahmen gearbeitet wird.

7.5. Beispiel 1

Das erste Beispiel [exceptions-01.php] veranschaulicht sowohl PHP-Fehler als auch eine Ausnahme:


<?php
 
// display all errors
ini_set("error_reporting", E_ALL);
ini_set("display_errors", "on");
// code --------
$var=[];
// unknown key
print $var["abcd"];
// division by zero
$var=7/0;
var_dump($var);
// fixed terminal board
$array = new \SplFixedArray(5);
$array[1] = 2;
$array[4] = "foo";
// index outside the limits
$array[5]=8;

Kommentare

  • Zeile 4: Wir weisen PHP an, alle Fehler zu melden. Der zweite Parameter ist die gewünschte Fehlerstufe:

Image

Image

  • Zeile 5: Wir weisen an, Fehler auf der Konsole anzuzeigen;
  • Zeile 9: Wir greifen auf ein nicht vorhandenes Element des Arrays [$var] zu;
  • Zeile 11: Wir führen eine Division durch Null durch;
  • Zeile 14: Wir erstellen eine Instanz der Klasse [SplFixedArray]. Diese Klasse ermöglicht es uns, ein Array mit festen Grenzen und ganzzahligen Indizes zu erstellen;
  • Zeile 18: Wir greifen auf ein nicht vorhandenes Element des Arrays zu;

Ergebnisse

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

Kommentare

  • Zeile 1 der Ergebnisse: Der Zugriff auf einen nicht vorhandenen Array-Schlüssel verursacht einen PHP-Fehler der Stufe [E_NOTICE]. Dies unterbricht die Skriptausführung nicht;
  • Zeile 3 der Ergebnisse: Die Division einer Zahl durch Null verursacht einen PHP-Fehler der Stufe [E_WARNING]. Dies unterbricht die Ausführung des Skripts nicht;
  • Zeilen 6–9 der Ergebnisse: Der Zugriff auf einen nicht vorhandenen Index eines Arrays [SplFixedArray] löst eine [RuntimeException] aus und unterbricht die Skriptausführung;

7.6. Behandlung von Ausnahmen

Das Skript [exceptions-02.php] zeigt, wie Ausnahmen behandelt werden:


<?php
 
// all errors are displayed
ini_set("error_reporting", E_ALL);
ini_set("display_errors", "on");
// surround the code with a try / catch
try {
  $var = [];
  // unknown key
  print $var["abcd"];
  // division by zero
  $var = 7 / 0;
  var_dump($var);
  // fixed terminal board
  $array = new \SplFixedArray(5);
  $array[1] = 2;
  $array[4] = "foo";
  // index outside the limits
  $array[5] = 8;
  // check
  print "ce message ne sera pas affiché\n";
} catch (\Throwable $ex) {
  // \Throwable is the interface implemented by most errors and exceptions
  // exception is displayed
  print "erreur, message : " . $ex->getMessage() . ", type : " . get_class($ex) . "\n";
}

Kommentare

  • Dieses Skript ist das im vorigen Absatz vorgestellte. Allerdings haben wir nun den Code in den Zeilen 8–19 – der Fehler verursachen könnte – in einen try/catch-Block eingeschlossen: Wenn der Code in den Zeilen 8–21 eine Ausnahme oder einen Fehler verursacht (auslöst), wird dieser durch die catch-Klausel in den Zeilen 22–26 behandelt;
  • Zeile 22: Der Parameter der [catch]-Klausel ist der Typ der zu behandelnden Ausnahme oder des Fehlers. Indem wir den Typ als [\Throwable] – eine Schnittstelle – angeben, zeigen wir an, dass wir jede Klasseninstanz behandeln wollen, die die [\Throwable]-Schnittstelle implementiert. Da alle Fehler- und Ausnahmeklassen diese Schnittstelle implementieren, behandelt die [catch]-Klausel hier jeden in einer Klasse gekapselten Fehler oder jede Ausnahme;
  • Zeile 19: Die Anweisung, die den Fehler auslöst und die Ausnahme auslöst. Sobald eine Ausnahme auftritt, springt die Ausführung zur [catch]-Klausel. Der Code nach Zeile 19 wird daher nicht ausgeführt;

Ergebnisse

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

Kommentare zu den Ergebnissen

  • Zeilen 1 und 3: Wir sehen Fehler der Stufen [E_NOTICE] und [E_WARNING]. Diese Fehler sind keine Ausnahmen und werden daher nicht von der [catch]-Klausel behandelt;
  • Zeile 5: Wir sehen die in der [catch]-Klausel angegebene Fehlermeldung. Es ist also eine von [\Exception] abgeleitete Ausnahme oder ein von [\Error] abgeleiteter Fehler aufgetreten. Hier sehen wir, dass es sich um die Klasse [\RuntimeException] handelt;

7.7. Parameter der [catch]-Klausel

Betrachten wir das folgende Skript [exceptions-03.php]:


<?php
 
// all errors are displayed
ini_set("error_reporting", E_ALL);
ini_set("display_errors", "on");
 
// a fixed terminal board
$array = new \SplFixedArray(5);
try {
  // index outside the limits
  $array[5] = 8;
} catch (\Throwable $ex) {
  // error message display
  print "Erreur 1 : " . $ex->getMessage() . "\n";
}
 
try {
  // index outside the limits
  $array[5] = 8;
} catch (\Exception $ex) {
  // error message display
  print "Erreur 2 : " . $ex->getMessage() . "\n";
}
 
try {
  // index outside the limits
  $array[5] = 8;
} catch (\RuntimeException $ex) {
  // error message display
  print "Erreur 3 : " . $ex->getMessage() . "\n";
}
try {
  // division by 0
  intdiv(5, 0);
} catch (\Throwable $ex) {
  // error message display
  print "Erreur 4 : " . $ex->getMessage() . "\n";
}

try {
  // division by 0
  intdiv(5, 0);
} catch (\DivisionByzeroError $ex) {
  // error message display
  print "Erreur 5 : " . $ex->getMessage() . "\n";
}
 
try {
  // division by 0
  intdiv(5, 0);
} catch (\Error $ex) {
  // error message display
  print "Erreur 6 : " . $ex->getMessage() . "\n";
}
 
try {
  // division by 0
  intdiv(5, 0);
} catch (\Exception $ex) {
  // error message display
  print "Erreur 6 : " . $ex->getMessage() . "\n";
}

Kommentare

  • Zeilen 8–31: 3 verschiedene Möglichkeiten, die Ausnahme zu behandeln, die durch die Verwendung eines falschen Indexes mit der Klasse [\SplFixedArray] ausgelöst wird. Wir haben gesehen, dass dieser Fehler eine [RuntimeException] auslöst;
    • Zeile 12: Behandelt einen Fehler vom Typ [\Throwable]. Dies ist zulässig, da der Typ [RuntimeException] vom Typ [\Exception] abgeleitet ist, der die Schnittstelle [\Throwable] implementiert;
    • Zeile 20: Behandelt einen Fehler vom Typ [\Exception]. Dies ist zulässig, da der Typ [RuntimeException] vom Typ [\Exception] abgeleitet ist;
    • Zeile 28: Behandelt einen Fehler vom Typ [\RuntimeException]. Dies ist die bevorzugte Methode, da es sich um den genauen Typ der ausgelösten Ausnahme handelt;
  • Zeilen 32–62: 4 verschiedene Möglichkeiten, die von der Funktion [intdiv] generierte Ausnahme zu behandeln, wenn ihr ein Divisor gleich 0 übergeben wird. Die Funktion [intdiv(int $dividend, int $divisor): int] führt die ganzzahlige Division $dividend / $divisor durch. Wenn der Divisor Null ist, wird die Ausnahme [\DivisionByzeroError] ausgelöst;
    • Zeile 35: Wir fangen jeden Fehler ab, der die Schnittstelle [\Throwable] implementiert. Dies ist zulässig;
    • Zeile 43: Wir fangen den genauen Typ des Fehlers ab: Dies ist die bevorzugte Methode;
    • Zeile 51: Der Typ [\Error] wird abgefangen. Dies ist zulässig, da die Klasse [DivisionByzeroError] die Klasse [Error] erweitert;
    • Zeile 59: Der Typ [\Exception] wird abgefangen. Dies ist ungültig, da die Klasse [DivisionByzeroError] keine Verbindung zur Klasse [\Exception] hat;

Ergebnisse

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. [finally]-Klausel

Die try/catch-Struktur kann ein drittes Element enthalten und zu einer try/catch/finally-Struktur werden. Der Code in der [finally]-Klausel wird in den folgenden zwei Fällen ausgeführt:

  • Die [try]-Klausel löst keine Ausnahme aus. Sie wird dann vollständig ausgeführt, und die Ausführung des Codes geht zur [finally]-Klausel über, die vollständig ausgeführt wird;
  • Die [try]-Klausel löst eine Ausnahme aus. Sie wird dann bis zu der Anweisung ausgeführt, die die Ausnahme auslöst. Die Ausführung geht dann zur [catch]-Klausel über, die vollständig ausgeführt wird. Die Ausführung geht dann zur [finally]-Klausel über, die vollständig ausgeführt wird;

Schließlich wird der Code im [finally]-Block immer ausgeführt. Dieses Szenario ist im folgenden Fall nützlich:

  • Im [try]-Block hat der Code Ressourcen (Dateien, Datenbanken, Netzwerkverbindungen, Warteschlangen) angefordert. Diese Ressourcen sind in der Regel speicherintensiv. Sie müssen daher so schnell wie möglich freigegeben (meist als „geschlossen“ bezeichnet) werden;
  • Wenn Ressourcen im [try]-Block angefordert wurden, sollte ihre Freigabe im [finally]-Block erfolgen. Dies stellt sicher, dass in allen Fällen (unabhängig davon, ob ein Fehler auftritt oder nicht) die angeforderten Ressourcen wieder an das System zurückgegeben werden;

Das folgende Skript [examples/exceptions/exceptions-04.php] veranschaulicht, wie die [finally]-Klausel in verschiedenen Situationen funktioniert:


<?php
 
// or create an exception instance
$e = new \Exception("Erreur…");    
var_dump($e);
 
// first test
try {
  print "Premier test\n";
  throw $e;
} catch (\Exception $ex1) {
  print $ex1->getMessage() . "\n";
} finally {
  print "Terminé\n";
}

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

Code-Kommentare

  • Zeile 4: $e ist eine Instanz der vordefinierten Klasse [\Exception]. Wir werden sie an verschiedenen Stellen auslösen;
  • Zeilen 8–15: Die Ausnahme $e wird im [try]-Block ausgelöst (Zeile 10);
  • Zeile 11: Die [\Exception]-Ausnahme wird abgefangen und ihre Fehlermeldung wird in die Konsole geschrieben;
  • Zeilen 13–15: Die [finally]-Klausel gibt eine Meldung aus. Basierend auf dem zuvor Gesagten sollte diese Meldung immer ausgegeben werden, unabhängig davon, ob im [try]-Block ein Fehler auftritt;
  • Zeilen 18–24: Im [try]-Block liegt kein Fehler vor. Wir sollten hier ebenfalls in den [finally]-Block eintreten;
  • Zeilen 27–34: Im `try`-Block befindet sich eine `return`-Anweisung, und es liegt kein Fehler vor. Man könnte sich daher fragen, ob wir in die `finally`-Klausel gelangen. Die Ausführung zeigt, dass dies der Fall ist;

Ergebnisse

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

Betrachten wir einen weiteren Fall [exceptions-05.php]:


<?php
 
// fourth test
try {
  print "Quatrième test\n";
  exit;
} finally {
  print "Terminé\n";
}

Kommentare

  • Zeile 6: Die Anweisung [exit] beendet die Ausführung des Skripts sofort: Die [finally]-Klausel wird nicht ausgeführt;
  • Zeilen 4–9: Ein Beispiel für try / catch / finally ohne [catch]-Klausel. Dies ist möglich;

Ergebnisse

Quatrième test

7.9. Eigene Ausnahmeklassen erstellen

In einem etwas größeren Projekt ist es sinnvoll, zwischen verschiedenen Fehlern zu unterscheiden, indem man sie in unterschiedliche Ausnahmeklassen kapselt. Im vorherigen Skript haben wir gesehen, dass jede Ausnahme mit einer [catch (\Throwable)]-Klausel abgefangen werden kann. Dies wird empfohlen, wenn Sie keine Ahnung haben, um welchen Fehler es sich handelt, und die Behandlung für alle Fehler gleich ist. Dies ist manchmal der Fall, aber oft muss man die Behandlung an den genauen Fehlertyp anpassen. Sie müssen dann zwischen den Fehlern unterscheiden.

Betrachten wir das folgende Skript [exceptions-06.php]:


<?php
 
// we define our own family of exceptions
class Exception1 extends \RuntimeException {
  
}
 
class Exception2 extends \RuntimeException {
  
}
 
// or use our exceptions
$e1 = new Exception1("Erreur1…");
var_dump($e1);
$e2 = new Exception2("Erreur2…");
var_dump($e2);
 
// first test
print ("premier test\n");
try {
  // throw an 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";
}
 
// second test
print ("second test\n");
try {
  // throw an Exception2 type
  throw $e2;
} catch (Exception1 $ex1) {
  print "Exception 1" . "\n";
  print $ex1->getMessage() . "\n";
} catch (Exception2 $ex2) {
  print "Exception 2" . "\n";
  print $ex2->getMessage() . "\n";
}
 
// third test
print ("troisième test\n");
try {
  // throw an Exception1 type
  throw $e1;
} catch (Exception1 | Exception2 $ex) {
  print "Exception 1 ou 2" . "\n";
  print $ex->getMessage() . "\n";
}
 
// fourth test
print ("quatrième test\n");
try {
  // throw an Exception2 type
  throw $e2;
} catch (Exception1 | Exception2 $ex) {
  print "Exception 1 ou 2" . "\n";
  print $ex->getMessage() . "\n";
}

Kommentare

  • Zeilen 4–10: Wir definieren zwei Klassen, [Exception1] und [Exception2], die beide von der vordefinierten Klasse [\RuntimeException] abgeleitet sind. Der Körper dieser Klassen ist leer. Mit anderen Worten: Wir verwenden sie nur, um ihre Typen zue ieren: Da sie unterschiedliche Typen haben, können wir in den [catch]-Klauseln zwischen diesen beiden Ausnahmen unterscheiden;
  • Zeilen 13–16: Wir definieren zwei Variablen, $e1 und $e2, mit den Typen [Exception1] bzw. [Exception2];
  • Zeilen 20–29: Wir haben eine try/catch/catch-Struktur. Dies ermöglicht es uns, verschiedene Ausnahmetypen mit unterschiedlichen [catch]-Klauseln zu behandeln;
  • Zeile 23: fängt Ausnahmen vom Typ [Exception1] ab;
  • Zeile 26: fängt Ausnahmen vom Typ [Exception2] ab;
  • Zeile 49: fängt Ausnahmen vom Typ [Exception1] oder (|) [Exception2] ab;

Ergebnisse

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

Kommentare zu den Ergebnissen

  • Zeilen 1–17: der „Inhalt“ einer Ausnahme:
    • Zeilen 2–3: die Fehlermeldung;
    • Zeilen 6–7: der Fehlercode;
    • Zeilen 8–9: der Name der Datei, in der die Ausnahme aufgetreten ist;
    • Zeilen 10–11: die Zeile, in der die Ausnahme aufgetreten ist;
    • Zeilen 15–16: die vorherige Ausnahme. Eine Ausnahme kann eine andere Ausnahme kapseln und so einen Ausnahmestapel definieren. Das Attribut [previous] ermöglicht den Zugriff auf diesen Stapel;

7.10. Eine Ausnahme erneut auslösen

Eine Ausnahme kann mehrfach ausgelöst werden, wie im folgenden Skript [exceptions-07.php] gezeigt:


<?php
 
try {
  try {
    // throw an exception
    throw new \Exception("test");
  } catch (\Exception $ex) {
    // the intercepted exception is re-launched
    throw $ex;
  } finally {
    // we'll make it to the finally
    print "finally 1\n";
  }
} catch (\Exception $ex2) {
  // the initial exception is recovered
  print $ex2->getMessage() . " dans try / catch / finally externe\n";
} finally {
  // we'll make it to the finally
  print "finally 2\n";
}

Kommentare

  • Zeile 6: Wir lösen eine Ausnahme aus;
  • Zeile 7: Wir fangen sie ab;
  • Zeile 9: Wir werfen sie erneut. Sie durchläuft dann den try/catch/finally-Block auf der obersten Ebene;
  • Zeile 14: Sie wird erneut abgefangen;
  • Zeilen 10–12: Die Ausführung zeigt, dass die Steuerung auch nach dem [throw] in Zeile 9 tatsächlich in die [finally]-Klausel des try/catch/finally-Blocks fließt;

Ergebnisse

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

7.11. Arbeiten mit einem Ausnahmestapel

Eine Ausnahme kann eine andere Ausnahme kapseln, die wiederum eine weitere kapseln kann, wodurch letztendlich ein Ausnahmestapel entsteht. Hier ist ein Beispiel [exceptions-08.php]:

<?php

// we define our own family of exceptions
class Exception1 extends \RuntimeException {

}

class Exception2 extends \RuntimeException {

}

class Exception3 extends \RuntimeException {

}

// or use our exceptions
$e1 = new Exception1("Erreur 1…", 1, new Exception2("Erreur 2…", 2, new Exception3("Erreur 3…")));
var_dump($e1);
// exploiting the current exception
print $e1->getMessage() . "\n";
$e = $e1;
while ($e->getPrevious() !== NULL) {
  // previous exception
  $e = $e->getPrevious();
  // error message
  print $e->getMessage() . "\n";
}

Kommentare

  • Zeilen 4–14: Definieren Sie drei Ausnahmeklassen, die von der vordefinierten Ausnahme [RuntimeException] abgeleitet sind;
  • Zeile 17: Eine Instanz der Klasse [Exception3] wird in eine Instanz der Klasse [Exception2] gekapselt, die ihrerseits in eine Instanz der Klasse [Exception1] gekapselt ist. Der hier verwendete Konstruktor ist der Konstruktor der Klasse [Exception]:

Image

Der dritte Parameter des Konstruktors ermöglicht die Kapselung einer Ausnahme. Dies kann im folgenden Szenario nützlich sein:

  • Wir definieren eine Methode M, die aus Kompatibilitätsgründen, beispielsweise mit einer Schnittstelle, eine Ausnahme vom Typ [Exception1] und nur diesen Typ erzeugen kann;
  • innerhalb der Methode M können jedoch auch andere Ausnahmetypen auftreten. Um einen Fehler an den Code zurückzugeben, der die Methode M aufruft, kapseln wir diese Ausnahmen dann in den Typ [Exception1] ein, den wir auslösen. Dadurch wird sichergestellt, dass die in der gekapselten Ausnahme enthaltenen Informationen – die die ursprüngliche Ursache des Fehlers waren – nicht verloren gehen;
  • Die Zeilen 20–27 zeigen, wie der Stapel von Ausnahmen verwaltet wird, die in einer Ausnahme verschachtelt sind;

Ergebnisse


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…