Bir catch bloğunda birden fazla istisna türü yakalamak


244

Aşağıdaki işlevselliği elde etmek, yakalamak AErrorve BErrorbir blokta daha temiz bir yol istiyorum :

try
{
    /* something */
}
catch( AError, BError $e )
{
    handler1( $e )
}
catch( Exception $e )
{
    handler2( $e )
}

Bunu yapmanın bir yolu var mı? Yoksa onları ayrı olarak yakalamak zorunda mıyım?

AErrorve Berrorortak bir temel sınıf var, ama aynı zamanda düşmek istediğim diğer türlerle de paylaşıyorlar handler2, bu yüzden sadece temel sınıfı yakalayamıyorum.


7
Bunu yalnızca bir yan not olarak eklemek için: Birden fazla istisna yakalamak için bir RFC dosyalanmıştır. Bu özelliğin PHP diline girip
girmediğini görelim

10
^ Bu özellik PHP 7.1
Subin

Yanıtlar:


353

Güncelleme:

PHP 7.1'den itibaren bu kullanılabilir.

Sözdizimi:

try
{
    // Some code...
}
catch(AError | BError $e)
{
    // Handle exceptions
}
catch(Exception $e)
{
    // Handle the general case
}

Dokümanlar: https://www.php.net/manual/en/language.exceptions.php#example-287

RFC: https://wiki.php.net/rfc/multiple-catch

Tamamlama: https://github.com/php/php-src/commit/0aed2cc2a440e7be17552cc669d71fdd24d1204a


7.1'den önceki PHP için:

Bu cevaplar söylediklerine rağmen, yakalamak AErrorve BErroraynı blokta (eğer istisnalar tanımlayan biri ise biraz daha kolaydır). "Düşmek" istediğiniz istisnalar olsa bile, yine de ihtiyaçlarınıza uygun bir hiyerarşi tanımlayabilmelisiniz.

abstract class MyExceptions extends Exception {}

abstract class LetterError extends MyExceptions {}

class AError extends LetterError {}

class BError extends LetterError {}

Sonra:

catch(LetterError $e){
    //voodoo
}

Burada ve burada görebileceğiniz gibi , SPLvarsayılan istisnalar bile kaldırabileceğiniz bir hiyerarşiye sahiptir. Ayrıca, PHP El Kitabında belirtildiği gibi :

Bir istisna atıldığında, ifadeyi izleyen kod yürütülmez ve PHP ilk eşleşen catch bloğunu bulmaya çalışır.

Bu, ayrıca

class CError extends LetterError {}

Eğer farklı işlemek zorunda olduğu AErrorya BError, senin catch ifadesi şu şekilde görünecektir böylece:

catch(CError $e){
    //voodoo
}
catch(LetterError $e){
    //voodoo
}

Aynı süper sınıfa meşru olarak ait olan yirmi veya daha fazla istisna olduğu durumda ve bunlardan beşini (veya büyük ish grubunu) bir şekilde ve diğerlerini halletmeniz gerekiyorsa, bunu YAPABİLİRSİNİZ.

interface Group1 {}

class AError extends LetterError implements Group1 {}

class BError extends LetterError implements Group1 {}

Ve sonra:

catch (Group1 $e) {}

İstisnalar söz konusu olduğunda OOP kullanımı çok güçlüdür. Saldırı gibi get_classveya instanceofolmayan şeyler kullanmak ve mümkünse kaçınılmalıdır.

Eklemek istediğim bir diğer çözüm de istisna işleme işlevselliğini kendi yöntemine koymak.

Olabilir

function handleExceptionMethod1(Exception $e)
{
    //voodoo
}

function handleExceptionMethod2(Exception $e)
{
    //voodoo
}

İstisna sınıfı hiyerarşilerini veya arayüzlerini kontrol etmenin kesinlikle bir yolu olmadığını varsayarsak (ve neredeyse her zaman bir yol olacaktır ), aşağıdakileri yapabilirsiniz:

try
{
    stuff()
}
catch(ExceptionA $e)
{
    $this->handleExceptionMethod1($e);
}
catch(ExceptionB $e)
{
    $this->handleExceptionMethod1($e);
}
catch(ExceptionC $e)
{
    $this->handleExceptionMethod1($e);
}
catch(Exception $e)
{
    $this->handleExceptionMethod2($e);
}

Bu şekilde, istisna işleme mekanizmanızın değişmesi gerekiyorsa değiştirmek zorunda olduğunuz tek bir kod konumunuz vardır ve OOP'nin genel yapıları içinde çalışıyorsunuzdur.


4
İşte bunun doğru cevabı olarak başka bir oy. Ne yazık ki kabul edilen cevapta söylenenler ve doğru cevap olarak kabul edilmesi, PHP'yi delilik yapan şeydir.
borfast

Bu kabul edilen cevap olmalı. Bununla birlikte, dosyaları değiştirebildiğinizi varsayar. AErrorüçüncü bir tarafça güncellenen bir kütüphane / dosyaya uygulanabilir.
Kayla

@ WaffleStealer654 Dosyaları doğrudan düzenleyemeseniz bile, dosyaları yine de alt sınıflandırabilir ve grubunuzu uygulamalarını sağlayabilirsiniz. Bu, istisnaları atabileceğinizi varsayar, ancak istisnanın atılacağı en temel düzey mekanizmayı sarabilir ve sonra yakalayabilir ve sarılmış istisnanızı atabilirsiniz.
MirroredFate

3
Bu kabul edilen cevap değildir, çünkü üçüncü taraf bir kütüphane kullandığınızda bunu yapamazsınız.
Denis V

@DenisV Yukarıdaki yorumuma bakın. Kurumsal yazılımlarda her zaman yapılır. Kapsülleme harika.
MirroredFate

229

PHP> = 7.1'de bu mümkündür. Bkz cevabı aşağıda.


İstisnaları değiştirebiliyorsanız bu yanıtı kullanın .

Yapamıyorsanız, hepsini yakalamayı deneyebilir Exceptionve ardından hangi istisnanın atıldığını kontrol edebilirsiniz instanceof.

try
{
    /* something */
}
catch( Exception $e )
{
    if ($e instanceof AError OR $e instanceof BError) {
       // It's either an A or B exception.
    } else {
        // Keep throwing it.
        throw $e;
    }
}

Ancak , yukarıda belirtilen cevapta açıklandığı gibi birden fazla catch bloğu kullanmak muhtemelen daha iyi olacaktır .

try
{
    /* something */
}
catch( AError $e )
{
   handler1( $e );
}
catch ( BError $b )
{
   handler2( $e );
}

6
Ben de bundan korkuyordum. Birlikte ele alınması gereken birçok hata türü varsa, bunları bir arada yakalamak ve türü test etmek iyi olurdu, ancak benim durumumda olduğu gibi sadece 2 için bunları ayrı ayrı yakalamak daha temiz. Teşekkürler!
Dominic Gurto

3
@DominicGurto: Evet, ben de onunla giderdim :) PHP'nin bir finallyifadeye karşı tutumu ile daha fazla ilgilenirim . ;)
alex

7
Ancak bunun TÜM istisnaları yakaladığını unutmayın, bu yüzden ... } else { throw($e); }ikisiyle eşleşmiyor gibi bir şey olmalı . Belki yanlış sözdizimi için üzgünüm, bir süre php görmedim.
Dalibor Filus

11
Burada ilk paragrafı okuduysanız : php.net/manual/en/language.exceptions.php birden fazla catch bloğunun mümkün olduğunu ve mükemmel geçerli bir çözüm olduğunu göreceksiniz. OP, bir catch ifadesine yanlışlıkla iki istisna sınıfı koymuştu. Cevabınızı birden fazla catch bloğu içeren başka bir örnekle güncellemenin daha iyi olacağını düşünüyorum.
Haralan Dobrev

4
Diğer İstisnalarınızı yiyen bir çözüm
önermek

88

Geliyor PHP 7.1 birden fazla türde yakalamak için yeteneğidir.

Böylece bu:

<?php
try {
    /* ... */
} catch (FirstException $ex) {
    $this->manageException($ex);
} catch (SecondException $ex) {
    $this->manageException($ex);
}
?>

ve

<?php
try {

} catch (FirstException | SecondException $ex) {
    $this->manageException($ex);
}
?>

işlevsel olarak eşdeğerdir.


45

PHP 7.1'den itibaren,

catch( AError | BError $e )
{
    handler1( $e )
}

ilginç olarak şunları da yapabilirsiniz:

catch( AError | BError $e )
{
    handler1( $e )
} catch (CError $e){
    handler2($e);
} catch(Exception $e){
    handler3($e);
}

ve PHP'nin önceki sürümlerinde:

catch(Exception $ex){
    if($ex instanceof AError){
        //handle a AError
    } elseif($ex instanceof BError){
        //handle a BError
    } else {
       throw $ex;//an unknown exception occured, throw it further
    }
}

25

Bu makale electrictoolbox.com/php-catch-multiple-exception-types sorusunu kapsar . Doğrudan makaleden kopyalanan yayının içeriği:

Örnek istisnalar

Bu örnek için tanımlanmış bazı istisnalar şunlardır:

class FooException extends Exception 
{
  public function __construct($message = null, $code = 0) 
  {
    // do something
  }
}

class BarException extends Exception 
{
  public function __construct($message = null, $code = 0) 
  {
    // do something
  }
}

class BazException extends Exception 
{
  public function __construct($message = null, $code = 0) 
  {
    // do something
  }
}

Birden fazla istisnayı işleme

Çok basit - atılabilecek her istisna türü için bir yakalama bloğu olabilir:

try 
{
  // some code that might trigger a Foo/Bar/Baz/Exception
}

catch(FooException $e) 
{
  // we caught a foo exception
}

catch(BarException $e) 
{
  // we caught a bar exception
}

catch(BazException $e) 
{
  // we caught a baz exception
}

catch(Exception $e) 
{
  // we caught a normal exception
  // or an exception that wasn't handled by any of the above
}

Diğer catch deyimlerinden hiçbiri tarafından işlenmeyen bir istisna atılırsa, catch (Exception $ e) bloğu tarafından işlenir. Sonuncusu olmak zorunda değildir.


3
Bu yöntemle ilgili sorun, iki veya daha fazla farklı özel durum için aynı kodu yürütmeniz gerektiğinde ortaya çıkar.
Parziphal

Bu, Elektrikli Araç Kutusu'ndan alındı . Kredi vermek için postayı düzenleme.
Kayla

PHP 7.x ile catch (Throwable $e)tüm istisnaları yakalamanız gerekir . Ayrıca bakınız: php.net/manual/tr/class.throwable.php
Mikko

21

Kabul edilen cevabın bir uzantısı olarak, orijinal örneğe benzer bir kalıba neden olan İstisna türünü değiştirebilirsiniz:

try {

    // Try something

} catch (Exception $e) {

    switch (get_class($e)) {

        case 'AError':
        case 'BError':
            // Handle A or B
            break;

        case 'CError':
            // Handle C
            break;

        case default:
            // Rethrow the Exception
            throw $e;

    }

}

6
bu çözüm yerine birden fazla kilit kullanın.
Alejandro Moreno

5

İstisnaları tanımlama üzerinde kontrolünüz yoksa makul bir alternatif. İstisnalar yakalandıklarında kategorilere ayırmak için istisna değişkeninin adını kullanın. Ardından try / catch bloğundan sonra istisna değişkenini kontrol edin.

$ABError = null;
try {
    // something
} catch (AError $ABError) {  // let the exception fall through
} catch (BError $ABError) {  // let the exception fall through
} catch (Exception $e) {
    handler2($e);
}
if ($ABError) {
    handler1($ABError);
}

Bu biraz garip görünümlü yaklaşım muhtemelen sadece catch bloğu uygulamaları arasında çok fazla çoğaltma varsa buna değer.


3

Düşüşün yanı sıra, goto kullanarak da adım atmak mümkündür . Dünyanın yanmasını görmek istiyorsanız çok faydalıdır.

<?php

class A_Error extends Exception {}
class B_Error extends Exception {}
class C_Error extends Exception {}

try {
    throw new A_Error();
} 
catch (A_Error $e) { goto abc; }
catch (B_Error $e) { goto abc; }
catch (C_Error $e) {
abc:
    var_dump(get_class($e));
    echo "Gotta Catch 'Em All\n";
}

3v4l.org


1

Kullanmanın harika bir yolu set_exception_handler.

Uyarı!!! PHP 7 ile ölümcül hatalar için beyaz bir ölüm ekranı alabilirsiniz. Örneğin, nesne olmayan bir yöntemde bir yöntem çağırırsanız normalde elde edersiniz Fatal error: Call to a member function your_method() on nullve hata bildirimi açıksa bunu görmeyi beklersiniz.

Yukarıdaki hata yakalanmayacaktır catch(Exception $e). Yukarıdaki hata, tarafından ayarlanan herhangi bir özel hata işleyicisini tetiklemez set_error_handler.

catch(Error $e){ }PHP7'de hataları yakalamak için kullanmalısınız . . Bu yardımcı olabilir:

class ErrorHandler{
    public static function excep_handler($e)
    {
        print_r($e);
    }
}
set_exception_handler(array('ErrorHandler','excep_handler'));

1
... ya da sadece catch (Throwable $e) { ... }onunla yazabilir ve bitirebilirsiniz . Ayrıca bakınız: php.net/manual/tr/class.throwable.php
Mikko

0

Burada listelenmeyen bir başka seçenek code, bir istisnanın niteliğini kullanmaktır , böylece böyle bir şey yapabilirsiniz:

try {

    if (1 === $foo) {

         throw new Exception(sprintf('Invalid foo: %s', serialize($foo)), 1);
    }

    if (2 === $bar) {
        throw new Exception(sprintf('Invalid bar: %s', serialize($foo)), 2);
    }
} catch (Exception $e) {

    switch ($e->getCode()) {

        case 1:
            // Special handling for case 1
            break;

        case 2:
            // Special handling for case 2
            break;

        default:

            // Special handling for all other cases
    }
}

Ben aşağı itmedim, ama belki OOP safları kullanarak yeni istisna sınıfları oluşturmadı kızgın extends \Exception?
keyboardSmasher

Anladım. Bu benim çözümümün bütün mesele, sadece belirli bir istisna atmak için bir ad alanı oluşturmak için rasgele sınıflar oluşturmanız gerekmez. Eminim bu yüzden bir kod belirtme yeteneğini eklediler.
Mike Purcell

Ben de alçalmadım ama sanırım aşağıdan gelenler bunun soruyu cevaplamadığına inanıyorlar. Ben o okuyucuya temizlemek yapar şeyle cevap başlayan öneririm size soru anlamış ve hala kod akışı için toplam farklı bir yol önermek istiyorum. Bu cevap gerçekten "çoklu istisna türlerinin nasıl yakalanacağına " değil, "istisna için birden fazla farklı nedenin nasıl ele alınacağına" cevap vermez .
Mikko Rantalainen

0

Hmm, php sürümü 7.1'den daha düşük yazılmış birçok çözüm var.

İşte tüm istisnaları yakalamak istemeyen ve ortak arayüzler yapamayanlar için başka bir basit:

<?php
$ex = NULL
try {
    /* ... */
} catch (FirstException $ex) {
    // just do nothing here
} catch (SecondException $ex) {
    // just do nothing here
}
if ($ex !== NULL) {
    // handle those exceptions here!
}
?>
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.