web-dev-qa-db-ja.com

1つのcatchブロック内で複数の例外タイプを捕捉する

AErrorBErrorを1つのブロックにまとめるために、次の機能を取得するためのよりクリーンな方法が欲しいです。

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

これを行う方法はありますか?それとも別々に捕まえる必要がありますか?

AErrorBerrorは共有基本クラスを持っていますが、それらは私がhandler2にフォールオーバーしたい他の型ともそれを共有しているので、基本クラスをキャッチすることはできません。

210
Dominic Gurto

PHP> = 7.1では、これは可能です。下記の 答え を参照してください。


あなたが例外を修正することができるなら、 この答えを使ってください

それができない場合は、Exceptionですべてをキャッチしてから、 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;
    }
}

しかしそれはおそらくより良いでしょう 前述の答えで説明されているように複数のcatchブロックを使う

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

更新:

PHP 7.1以降、これは利用可能です。

構文は次のとおりです。

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

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

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


7.1より前のPHPの場合:

これらの他の答えが言うことにもかかわらず、あなたは同じブロックの中でAErrorBErrorを捕らえることができます(例外を定義している方がやや簡単です)。たとえあなたが「通過する」ことを望む例外があるとしても、あなたはまだあなたのニーズに合うように階層を定義することができるはずです。

abstract class MyExceptions extends Exception {}

abstract class LetterError extends MyExceptions {}

class AError extends LetterError {}

class BError extends LetterError {}

その後:

catch(LetterError $e){
    //voodoo
}

お分かりのように、 here および here を見てください。デフォルトのSPL例外でさえ、利用できる階層を持っています。さらに PHP Manual に書いてあるように:

例外が投げられると、ステートメントに続くコードは実行されず、PHPは最初にマッチしたcatchブロックを見つけようとします

これはまたあなたが持つことができることを意味します

class CError extends LetterError {}

これはAErrorBErrorとは異なる方法で処理する必要があるので、catchステートメントは次のようになります。

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

合法的に同じスーパークラスに属する20以上の例外があり、そのうち5つ(またはどんな大規模なグループでも)を処理する必要があり、それ以外はそれ以外の方法で処理する必要がある場合。

interface Group1 {}

class AError extends LetterError implements Group1 {}

class BError extends LetterError implements Group1 {}

その後:

catch (Group1 $e) {}

例外に関してはOOPを使うことは非常に強力です。 get_classinstanceofのようなものを使うことはハックであり、可能ならば避けるべきです。

私が追加したいもう一つの解決策は、独自のメソッドに例外処理機能を入れることです。

持っている可能性があります

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

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

例外クラスの階層やインタフェースを制御することは絶対に不可能である(そして方法であることがほとんど常にある)と仮定すると、次のことができます。

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);
}

このようにして、例外処理メカニズムを変更する必要がある場合でも、変更する必要があるコードの場所は1つだけであり、OOPの一般的な構成内で作業しています。

267
MirroredFate

PHP 7.1 でやってくるのは、複数の型を捕まえる能力です。

だからこれは:

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

そして

<?php
try {

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

機能的に同等です。

88
Joe Watkins

PHP 7.1以降

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

興味深いことに、次のこともできます。

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

そして以前のバージョンのPHPでは:

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
    }
}
42
hanshenrik

この記事は質問 electrictoolbox.com/php-catch-multiple-exception-types をカバーしています。記事から直接コピーされた投稿の内容:

例外の例

この例の目的のために定義されている例外の例をいくつか示します。

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
  }
}

複数の例外を処理する

それは非常に簡単です - 投げられることができるそれぞれの例外タイプのためにcatchブロックがあるかもしれません:

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
}

他のcatchステートメントのいずれによっても処理されない例外がスローされた場合、それはcatch(Exception $ e)ブロックによって処理されます。それは必ずしも最後のものである必要はありません。

25
user1983902

受け入れられた答えに対する拡張として、あなたは例外のタイプを変えることができて、元の例にいくぶん似ているパターンをもたらす:

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;

    }

}
21
smix96

例外の定義を制御できない場合は、これが妥当な代替策です。例外が発生したときに例外を分類するには、例外変数の名前を使用します。その後、try/catchブロックの後の例外変数を調べます。

$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);
}

このやや奇妙に見えるアプローチは、catchブロックの実装間に多くの重複がある場合にのみおそらく価値があります。

5
dellsala

フォールスルーの他に、gotoを使用してステップオーバーすることもできます。あなたが世界が燃えるのを見たいならば、それは非常に役に立ちます。

<?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";
}

v4l.org

3
ml_

良い方法はset_exception_handlerを使うことです。

警告!!! PHP 7を指定すると、致命的なエラーのために白い画面が表示されることがあります。たとえば、非オブジェクトに対してメソッドを呼び出すと、通常はFatal error: Call to a member function your_method() on nullが返され、エラー報告が有効になっていればこれが表示されるはずです。

上記のエラーはcatch(Exception $e)で捉えられません。上記のエラーはset_error_handlerで設定されたカスタムエラーハンドラをトリガーしません。

PHP7でエラーを検出するにはcatch(Error $e){ }を使わなければなりません。 。これは役に立ちます。

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

ここに記載されていない別のオプションは、例外のcode属性を使用することです。そのため、次のようなことができます。

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
    }
}
0
Mike Purcell

うーん、7.1より低いバージョンのphp用に書かれた多くの解決策があります。

これは、すべての例外をキャッチする必要がなく、一般的なインターフェイスを作成できない場合のための、もう1つの単純なものです。

<?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!
}
?>
0
GT.