Skip to content

7. İstisnalar ve hatalar

Bir sınıfın yöntemi düzeltilemez bir hatayla karşılaştığında (dosya yok, veritabanına bağlanılamadı, ağ bağlantısı kesik), konsolda (dosya, veritabanı) bir hata mesajı görüntülemez, bunun yerine bir istisna atar. Tüm istisnalar [\Exception] sınıfını genişletir. İstisnaların yanı sıra, PHP sınıfının iç işleyişi, temel sınıfı [\Error] olan hatalar da üretir. Her iki sınıf da PHP ve [\Throwable] arayüzlerini uygular.

7.1. Komut dosyası ağacı

Image

7.2. [\Throwable] arayüzü

[\Throwable] arayüzü şu şekildedir:

Image

Arayüzdeki yöntemlerin işlevleri şöyledir:

Image

7.3. PHP 7'de önceden tanımlanmış istisnalar

PHP 7'de tanımlanan istisnalar, çeşitli istisna sınıflarını tanımlamaktadır:

Image

  • [1]'te, PHP'te önceden tanımlanmış istisnalar;
  • [2]’te, PHP 7’nin SPL kütüphanesindeki (Standart PHP Kütüphanesi) istisnalar. SPL kütüphanesi, geliştiricilerin sıkça karşılaştığı sorunları çözmek amacıyla tasarlanmış sınıf ve arayüzlerden oluşan bir koleksiyondur.

7.4. PHP 7'de önceden tanımlanmış hatalar

PHP 7, çeşitli hata sınıflarını tanımlar:

Image

[\Error] sınıfı, PHP'te önceden tanımlanmış tüm hataların üst sınıfıdır. [ErrorException] sınıfı, [\Error] sınıfının bir örneğini [\Exception] sınıfının bir örneği içinde kapsüllemeyi sağlar. Bu, yalnızca istisnaları işleyerek hata yönetimini standartlaştırmaya olanak tanır.

7.5. Örnek 1

İlk örnek olan [exceptions-01.php], hem PHP hatalarını hem de bir istisnayı göstermektedir:


<?php

// tüm hataların görüntülenmesi
ini_set("error_reporting", E_ALL);
ini_set("display_errors", "on");
// kod --------
$var=[];
// bilinmeyen anahtar
print $var["abcd"];
// sıfıra bölme
$var=7/0;
var_dump($var);
// sabit sınırlı dizi
$array = new \SplFixedArray(5);
$array[1] = 2;
$array[4] = "foo";
// sınırların dışında indeks
$array[5]=8;

Açıklamalar

  • 4. satır: PHP'ten tüm hataları bildirmesi istenir. İkinci parametre, istenen hata seviyesidir:

Image

Image

  • 5. satır: Hataların konsolda görüntülenmesi isteniyor;
  • 9. satır: [$var] dizisinin var olmayan bir elemanına erişiliyor;
  • 11. satır: sıfıra bölme işlemi yapılıyor;
  • 14. satır: [SplFixedArray] sınıfının bir örneği oluşturuluyor. Bu sınıf, sabit sınırlı ve tamsayı indeksli bir dizi oluşturulmasına olanak tanır;
  • 18. satır: dizide var olmayan bir elemana erişiliyor;

Sonuçlar

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 {ana}
thrown in C:\Data\st-2019\dev\php7\php5-exemples\exemples\exceptions\exceptions-01.php on line 18

Yorumlar

  • sonuçların 1. satırı: Bir dizinin mevcut olmayan bir anahtarına erişilmeye çalışılması, [E_NOTICE] seviyesinde bir PHP hatasına neden olur. Bu, komut dosyasının yürütülmesini kesintiye uğratmaz;
  • sonuçların 3. satırı: Bir sayıyı sıfıra bölmek, [E_WARNING] seviyesinde bir PHP hatasına neden olur. Bu, komut dosyasının yürütülmesini kesintiye uğratmaz;
  • sonuçların 6-9. satırları: [SplFixedArray] dizisinin mevcut olmayan bir indeksine erişim, [RuntimeException] türünde bir istisna oluşturur ve komut dosyasının çalışmasını durdurur;

7.6. İstisnaları Yönetme

[exceptions-02.php] komut dosyası, istisnaların nasıl yönetileceğini gösterir:


<?php

// tüm hatalar görüntülenir
ini_set("error_reporting", E_ALL);
ini_set("display_errors", "on");
// kod, try / catch ile çevrelenir
try {
  $var = [];
  // bilinmeyen anahtar
  print $var["abcd"];
  // sıfıra bölme
  $var = 7 / 0;
  var_dump($var);
  // sabit sınırlı dizi
  $array = new \SplFixedArray(5);
  $array[1] = 2;
  $array[4] = "foo";
  // sınırların dışındaki indeks
  $array[5] = 8;
  // doğrulama
  print "ce message ne sera pas affiché\n";
} catch (\Throwable $ex) {
  // \Throwable, çoğu hata ve istisna tarafından uygulanan arayüzdür
  // istisna görüntülenir
  print "erreur, message : " . $ex->getMessage() . ", type : " . get_class($ex) . "\n";
}

Açıklamalar

  • Bu komut dosyası, önceki paragrafta sunulan komut dosyasıdır. Ancak şimdi, hata oluşturma olasılığı bulunan 8-19. satırlardaki kod, bir try / catch yapısı ile çevrelenmiştir: 8-21. satırlardaki kod bir istisna veya hata oluşturursa (atarsa), bu durum 22-26. satırlardaki catch cümlesi tarafından yönetilecektir;
  • 22. satır: [catch] cümlesindeki parametre, yönetmek istediğimiz istisna veya hata türüdür. Bir arayüz olan [\Throwable] türünü belirleyerek, [\Throwable] arayüzünü uygulayan tüm sınıf örneklerini yönetmek istediğimizi belirtiriz. Tüm hata ve istisna sınıfları bu arayüzü uyguladığından, [catch] cümlesi burada bir sınıf içinde kapsüllenmiş tüm hataları/istisnaları yönetir;
  • 19. satır: hatayı tetikleyen ve istisnayı oluşturan komut. Bir istisna oluştuğu anda, [catch] cümlesine yönlendirme yapılır. Dolayısıyla, 19. satırın sonrasındaki kod yürütülmeyecektir;

Sonuçlar

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

Sonuçlara ilişkin açıklamalar

  • 1. ve 3. satırlar: [E_NOTICE] ve [E_WARNING] düzeyindeki hatalar görülmektedir. Bu hatalar istisna değildir ve bu nedenle [catch] cümlesi tarafından yönetilmez;
  • 5. satır: [catch] cümlesinde yazan hata mesajı görülüyor. Dolayısıyla, [\Exception]’ten türetilen bir istisna ya da [\Error]’ten türetilen bir hata meydana gelmiştir. Burada bunun [\RuntimeException] sınıfına ait olduğunu görüyoruz;

7.7. [catch] cümlesinin parametreleri

Aşağıdaki [exceptions-03.php] komut dosyasını inceleyelim:


<?php

// tüm hatalar görüntülenir
ini_set("error_reporting", E_ALL);
ini_set("display_errors", "on");

// sabit sınırlı bir dizi
$array = new \SplFixedArray(5);
try {
  // sınırların dışındaki indeks
  $array[5] = 8;
} catch (\Throwable $ex) {
  // hata mesajı görüntüleme
  print "Erreur 1 : " . $ex->getMessage() . "\n";
}

try {
  // sınırların dışında bir indeks
  $array[5] = 8;
} catch (\Exception $ex) {
  // hata mesajı göstergesi
  print "Erreur 2 : " . $ex->getMessage() . "\n";
}

try {
  // değer sınırların dışında
  $array[5] = 8;
} catch (\RuntimeException $ex) {
  // hata mesajı görüntüleme
  print "Erreur 3 : " . $ex->getMessage() . "\n";
}
try {
  // 0 ile bölme
  intdiv(5, 0);
} catch (\Throwable $ex) {
  // hata mesajı görüntüleme
  print "Erreur 4 : " . $ex->getMessage() . "\n";
}

try {
  // 0 ile bölme
  intdiv(5, 0);
} catch (\DivisionByzeroError $ex) {
  // hata mesajı görüntüleme
  print "Erreur 5 : " . $ex->getMessage() . "\n";
}

try {
  // 0 ile bölme
  intdiv(5, 0);
} catch (\Error $ex) {
  // hata mesajı görüntüleme
  print "Erreur 6 : " . $ex->getMessage() . "\n";
}

try {
  // 0 ile bölme
  intdiv(5, 0);
} catch (\Exception $ex) {
  // hata mesajı görüntüleme
  print "Erreur 6 : " . $ex->getMessage() . "\n";
}

Açıklamalar

  • 8-31. satırlar: [\SplFixedArray] sınıfında yanlış bir indeks kullanılması sonucu ortaya çıkan istisnayı yönetmenin 3 farklı yolu. Bu hatanın [RuntimeException] türünde bir istisna oluşturduğunu görmüştük;
    • 12. satır: [\Throwable] türünde bir hatayı işler. [RuntimeException] türü, [\Exception] türünden türetilmiş ve bu tür de [\Throwable] arayüzünü uyguladığından, bu geçerlidir;
    • 20. satır: [\Exception] türündeki bir hatayı işler. [RuntimeException] türü, [\Exception] türünden türetildiğinden bu geçerlidir;
    • 28. satır: [\RuntimeException] türündeki bir hatayı işler. Oluşturulan istisnanın tam türü bu olduğu için tercih edilmesi gereken yöntem budur;
  • 32-62. satırlar: [intdiv] işlevine 0'a eşit bir bölücü aktarıldığında bu işlev tarafından oluşturulan istisnayı yönetmenin 4 farklı yolu. [ intdiv ( int $dividend , int $divisor ) : int] fonksiyonu, $dividend / $divisor tam sayı bölme işlemini gerçekleştirir. Bölücü sıfır olduğunda, [\DivisionByzeroError] istisnası tetiklenir;
    • 35. satır: [\Throwable] arayüzünü uygulayan tüm hatalar yakalanır. Bu geçerlidir;
    • 43. satır: hatanın tam türü yakalanır: tercih edilmesi gereken yöntem budur;
    • 51. satır: [\Error] türü yakalanır. [DivisionByzeroError] sınıfı, [Error] sınıfını genişlettiği için bu geçerlidir;
    • satır 59: [\Exception] türü yakalanır. [DivisionByzeroError] sınıfının [\Exception] sınıfıyla hiçbir bağlantısı olmadığı için bu geçersizdir;

Sonuçlar

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] maddesi

try / catch yapısı üçüncü bir öğeye sahip olabilir ve try / catch / finally yapısına dönüşebilir. [finally] cümlesindeki kod, aşağıdaki iki durumda da yürütülür:

  • [try] cümlesi bir istisna oluşturmaz. Bu durumda tamamen yürütülür ve ardından kodun yürütülmesi, tamamen yürütülen [finally] cümlesine geçer;
  • [try] cümlesi bir istisna oluşturur. Bu durumda, istisnayı oluşturan komuta kadar yürütülür. Kodun yürütülmesi daha sonra [catch] maddesine geçer ve bu madde tamamen yürütülür. Ardından kodun yürütülmesi [finally] maddesine geçer ve bu madde tamamen yürütülür;

Son olarak, [finally] cümlesindeki kod da her zaman yürütülür. Bu senaryo aşağıdaki durumda yararlıdır:

  • [try]'te kod, kaynaklar (dosyalar, veritabanları, ağ bağlantıları, kuyruklar) elde etmiştir. Genellikle bu kaynaklar bellek açısından yük oluşturur. Bu nedenle, mümkün olan en kısa sürede bu kaynaklar iade edilmelidir (genellikle "kapatmak" olarak ifade edilir);
  • kaynakların edinilmesi [try]'te gerçekleştirildiyse, bunların iadesi [finally]'te yapılır. Bu, her durumda (hata olsun ya da olmasın) edinilen kaynakların sisteme iade edilmesini garanti eder;

Aşağıdaki [exemples/exceptions/exceptions-04.php] komut dosyası, [finally] maddesinin çeşitli durumlarda nasıl işlediğini göstermektedir:


<?php

// veya bir istisna örneği oluşturur
$e = new \Exception("Erreur…");    
var_dump($e);

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

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

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

Kod açıklamaları

  • 4. satır: $e, önceden tanımlanmış [\Exception] sınıfının bir örneğidir. Bunu farklı yerlerde çalıştıracağız;
  • 8-15. satırlar: $e istisnası, [try] içinde (10. satır) tetiklenir;
  • 11. satır: [\Exception] istisnası yakalanır ve hata mesajı konsola yazılır;
  • satır 13-15: [finally] yorumu bir mesaj yazar. Yukarıda belirtildiği gibi, bu mesaj, [try]'te hata olsun ya da olmasın her zaman yazılmalıdır;
  • 18-24. satırlar: [try]'te hata yok. Burada da [finally]'e geçilmesi gerekir;
  • 27-34. satırlar: try bloğunda bir [return] komutu var ve hata yok. Bu durumda, [finally] bloğuna geçilip geçilmeyeceği sorusu akla gelebilir. Yürütme sonucu, evet geçildiğini gösteriyor;

Sonuçlar

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

Başka bir durumu inceleyelim: [exceptions-05.php]:


<?php

// dördüncü test
try {
  print "Quatrième test\n";
  exit;
} finally {
  print "Terminé\n";
}

Yorumlar

  • 6. satır: [exit] komutu, betiğin yürütülmesini derhal durdurur: [finally] bloğu yürütülmez;
  • 4-9. satırlar: [catch] cümlesi içermeyen bir try / catch / finally örneği. Bu mümkündür;

Sonuçlar

Quatrième test

7.9. Kendi istisna sınıflarınızı oluşturma

Biraz büyük bir projede, farklı hataları farklı istisna sınıfları içinde kapsülleyerek birbirinden ayırmak yararlıdır. Önceki komut dosyasında, her istisnanın bir [catch (\Throwable] cümlesi ile yakalanabileceğini gördük. Yakalanan hatanın ne olduğu hakkında hiçbir fikriniz yoksa ve tüm hatalar için işleme aynıysa bu yöntem önerilir. Bazen durum böyledir, ancak çoğu zaman işlemeyi hatanın tam türüne göre uyarlamak gerekir. Bu durumda hataları birbirinden ayırt etmek gerekir.

Aşağıdaki [exceptions-06.php] komut dosyasını inceleyelim:


<?php

// kendi istisna ailemizi tanımlıyoruz
class Exception1 extends \RuntimeException {
  
}

class Exception2 extends \RuntimeException {
  
}

// veya istisnalarımızı kullanıyoruz
$e1 = new Exception1("Erreur1…");
var_dump($e1);
$e2 = new Exception2("Erreur2…");
var_dump($e2);

// birinci test
print ("premier test\n");
try {
  // Exception1 türünü tetikliyoruz
  throw $e1;
} catch (Exception1 $ex1) {
  print "Exception 1" . "\n";
  print $ex1->getMessage() . "\n";
} catch (Exception2 $ex2) {
  print "Exception 2" . "\n";
  print $ex2->getMessage() . "\n";
}

// ikinci test
print ("second test\n");
try {
  // Exception2 türünde bir istisna oluşturuyoruz
  throw $e2;
} catch (Exception1 $ex1) {
  print "Exception 1" . "\n";
  print $ex1->getMessage() . "\n";
} catch (Exception2 $ex2) {
  print "Exception 2" . "\n";
  print $ex2->getMessage() . "\n";
}

// üçüncü test
print ("troisième test\n");
try {
  // Exception1 türü bir istisna atılıyor
  throw $e1;
} catch (Exception1 | Exception2 $ex) {
  print "Exception 1 ou 2" . "\n";
  print $ex->getMessage() . "\n";
}

// dördüncü test
print ("quatrième test\n");
try {
  // Exception2 türü bir istisna atılıyor
  throw $e2;
} catch (Exception1 | Exception2 $ex) {
  print "Exception 1 ou 2" . "\n";
  print $ex->getMessage() . "\n";
}

Açıklamalar

  • 4-10. satırlar: Her ikisi de önceden tanımlanmış [\RuntimeException] sınıfından türetilen [Exception1] ve [Exception2] adlı iki sınıf tanımlanmaktadır. Bu sınıfların gövdesi boştur. Başka bir deyişle, bunlar yalnızca türleri için kullanılır: bu iki istisnayı [catch] cümlelerinde birbirinden ayırt edebilmemiz, türlerinin farklı olmasından kaynaklanmaktadır;
  • 13-16. satırlar: sırasıyla [Exception1] ve [Exception2] türlerine sahip iki değişken, $e1 ve $e2 tanımlanmıştır;
  • 20-29. satırlar: Bir try / catch / catch yapısı vardır. Bu, farklı [catch] cümleleriyle farklı istisna türlerini yönetmeye olanak tanır;
  • 23. satır: [Exception1] türündeki istisnaları yakalar;
  • 26. satır: [Exception2] türündeki istisnaları yakalar;
  • 49. satır: [Exception1] veya (|) [Exception2] türündeki istisnaları yakalar;

Sonuçlar

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…

Sonuçlara ilişkin açıklamalar

  • satır 1-17: bir istisnanın ‘içeriği’:
    • 2-3. satırlar: hata mesajı;
    • 6-7. satırlar: hata kodu;
    • satır 8-9: istisnanın meydana geldiği dosyanın adı;
    • satır 10-11: istisnanın meydana geldiği satır;
    • satır 15-16: önceki istisna. Bir istisna, başka bir istisnayı kapsayabilir ve böylece bir istisna yığını oluşturabilir. [previous] özniteliği, bu yığını kullanmaya olanak tanır;

7.10. Bir istisnayı yeniden çalıştırma

Aşağıdaki [exceptions-07.php] komut dosyasında gösterildiği gibi, bir istisna birden fazla kez tetiklenebilir:


<?php

try {
  try {
    // bir istisna atılıyor
    throw new \Exception("test");
  } catch (\Exception $ex) {
    // yakalanan istisna yeniden atılıyor
    throw $ex;
  } finally {
    // finally bloğuna sorunsuzca geçiliyor
    print "finally 1\n";
  }
} catch (\Exception $ex2) {
  // ilk istisna başarıyla yakalanıyor
  print $ex2->getMessage() . " dans try / catch / finally externe\n";
} finally {
  // finally bloğuna sorunsuzca geçilecek
  print "finally 2\n";
}

Açıklamalar

  • 6. satır: bir istisna atılır;
  • 7. satır: istisna yakalanır;
  • 9. satır: istisna yeniden atılır. Ardından, üst düzeydeki try / catch / finally yapısına geçer;
  • 14. satır: istisna yeniden yakalanır;
  • satır 10-12: Yürütme, 9. satırdaki [throw]'ten sonra bile try / catch / finally yapısının [finally] kısmına geçildiğini göstermektedir;

Sonuçlar

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

7.11. İstisna yığınının kullanımı

Bir istisna, başka bir istisnayı kapsayabilir; bu istisna da başka bir istisnayı kapsayabilir ve sonuçta bir istisna yığını oluşur. İşte bir örnek [exceptions-08.php]:

<?php

// kendi istisna ailemizi tanımlıyoruz
class Exception1 extends \RuntimeException {

}

class Exception2 extends \RuntimeException {

}

class Exception3 extends \RuntimeException {

}

// veya istisnalarımızı kullanıyoruz
$e1 = new Exception1("Erreur 1…", 1, new Exception2("Erreur 2…", 2, new Exception3("Erreur 3…")));
var_dump($e1);
// mevcut istisnayı işliyoruz
print $e1->getMessage() . "\n";
$e = $e1;
while ($e->getPrevious() !== NULL) {
   // önceki istisna
  $e = $e->getPrevious();
   // hata mesajı
  print $e->getMessage() . "\n";
}

Açıklamalar

  • 4-14. satırlar: önceden tanımlanmış [RuntimeException] istisnasından türetilen üç istisna sınıfını tanımlar;
  • 17. satır: [Exception3] sınıfının bir örneği, [Exception2] sınıfının bir örneği içinde kapsüllenmiştir; bu da [Exception1] sınıfının bir örneği içinde kapsüllenmiştir. Burada kullanılan oluşturucu, [Exception] sınıfının oluşturucusudur:

Image

Oluşturucunun 3. parametresi, bir istisnayı kapsüllemeye olanak tanır. Bu, aşağıdaki senaryoda yararlı olabilir:

  • örneğin bir arayüzle uyumluluk nedeniyle, yalnızca [Exception1] türünde bir istisna üretebilen bir M yöntemi tanımlanır;
  • ancak M yönteminde başka türde istisnalar da meydana gelebilir. Bir hatayı M yöntemini çağıran koda iletmek için, bu istisnaları [Exception1] türünde kapsülleyip bu istisnayı atarız. Bu, kapsüllenmiş istisnada bulunan ve hatanın asıl nedeni olan bilginin kaybolmamasını sağlar;
  • 20-27. satırlar, bir istisna içindeki iç istisna yığınını nasıl yöneteceğimizi gösterir;

Sonuçlar


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…