web-dev-qa-db-fra.com

Attraper plusieurs types d'exceptions dans un bloc catch

Je voudrais un moyen plus propre d'obtenir les fonctionnalités suivantes, pour attraper AError et BError dans un bloc:

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

Est-ce qu'il y a un moyen de faire ça? Ou dois-je les attraper séparément?

AError et Berror ont une classe de base partagée, mais ils la partagent également avec d'autres types que je voudrais utiliser pour passer à handler2, je ne peux donc pas simplement attraper la classe de base. .

217
Dominic Gurto

Mise à jour:

À partir de PHP 7.1, cette option est disponible.

La syntaxe est la suivante:

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


Pour PHP avant 7.1:

Malgré ce que disent ces autres réponses, vous pouvez attraper AError et BError dans le même bloc (c'est un peu plus facile si vous définissez les exceptions). Même s'il y a des exceptions que vous voulez "passer à travers", vous devriez toujours pouvoir définir une hiérarchie pour répondre à vos besoins.

abstract class MyExceptions extends Exception {}

abstract class LetterError extends MyExceptions {}

class AError extends LetterError {}

class BError extends LetterError {}

Ensuite:

catch(LetterError $e){
    //voodoo
}

Comme vous pouvez le voir ici et ici , même les exceptions par défaut SPL ont une hiérarchie que vous pouvez exploiter. De plus, comme indiqué dans le Manuel PHP :

Quand une exception est levée, le code suivant l'instruction ne sera pas exécuté et PHP tentera de trouver le premier bloc catch correspondant.

Cela signifie que vous pourriez aussi avoir

class CError extends LetterError {}

que vous devez gérer différemment de AError ou BError, de sorte que votre déclaration catch ressemble à ceci:

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

Si vous aviez le cas où il y avait une vingtaine d'exceptions ou plus qui appartenaient légitimement à la même superclasse et que vous deviez en gérer cinq (ou un groupe quelconque) dans un sens et le reste dans l'autre, vous pouvez TOUJOURS le faire.

interface Group1 {}

class AError extends LetterError implements Group1 {}

class BError extends LetterError implements Group1 {}

Puis:

catch (Group1 $e) {}

Utiliser OOP pour les exceptions est très puissant. Utiliser des choses comme get_class ou instanceof sont des hacks, et devraient être évités si possible.

Une autre solution que je voudrais ajouter est de placer la fonctionnalité de gestion des exceptions dans sa propre méthode.

Tu aurais pu

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

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

En supposant qu'il n'y a absolument aucun moyen de contrôler les hiérarchies de classes d'exceptions ou les interfaces (et qu'il y a presque toujours un moyen), vous pouvez procéder comme suit:

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

De cette manière, vous ne disposez toujours que d'un seul emplacement de code que vous devez modifier si votre mécanisme de gestion des exceptions doit être modifié, et vous travaillez dans les structures générales de la programmation orientée objet.

280
MirroredFate

En PHP> = 7.1 c'est possible. Voir le réponse ci-dessous.


Si vous pouvez modifier les exceptions, tilisez cette réponse .

Si vous ne le pouvez pas, vous pouvez essayer de tout attraper avec Exception, puis vérifier quelle exception a été levée avec 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;
    }
}

Mais il serait probablement préférable de tilisez plusieurs blocs catch comme décrit dans la réponse ci-dessus .

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

À venir PHP 7.1 est la possibilité d’attraper plusieurs types.

Alors que ça:

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

et

<?php
try {

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

sont fonctionnellement équivalents.

88
Joe Watkins

À partir de PHP 7.1,

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

curieusement, vous pouvez aussi:

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

et dans les versions précédentes de 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

Cet article couvre la question electrictoolbox.com/php-catch-multiple-exception-types . Contenu de l'article copié directement de l'article:

Exemples d'exceptions

Voici quelques exemples d'exceptions définies aux fins de cet exemple:

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

Gestion de plusieurs exceptions

C'est très simple - il peut y avoir un bloc catch pour chaque type d'exception pouvant être levé:

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
}

Si une exception est générée qui n'est pas gérée par l'une des autres instructions catch, elle le sera par le bloc catch (Exception $ e). Ce n'est pas nécessairement le dernier.

25
user1983902

En tant qu'extension de la réponse acceptée, vous pouvez changer de type d'exception, ce qui donne un motif qui ressemble à celui de l'exemple d'origine:

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

Voici une alternative raisonnable si vous n'avez pas le contrôle sur la définition des exceptions. Utilisez le nom de la variable d'exception pour classer les exceptions lorsqu'elles sont interceptées. Ensuite, recherchez la variable d'exception après le bloc 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);
}

Cette approche quelque peu étrange n’en vaut probablement la peine que s’il ya beaucoup de duplication entre les implémentations de bloc de capture.

5
dellsala

En plus des retombées, il est également possible d’intervenir en utilisant goto . C'est très utile si vous voulez voir le monde brûler.

<?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_

Un bon moyen consiste à utiliser set_exception_handler.

Attention!!! avec PHP 7, vous pourriez obtenir un écran blanc de la mort pour les erreurs fatales. Par exemple, si vous appelez une méthode sur un non-objet, vous obtiendrez normalement Fatal error: Call to a member function your_method() on null et vous vous attendez à le voir si le rapport d'erreur est activé.

L'erreur ci-dessus ne sera PAS interceptée avec catch(Exception $e). L'erreur ci-dessus NE déclenchera PAS un gestionnaire d'erreurs personnalisé défini par set_error_handler.

Vous devez utiliser catch(Error $e){ } pour intercepter les erreurs en PHP7. . Cela pourrait aider:

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

Une autre option non répertoriée ici consiste à utiliser l'attribut code d'une exception afin de pouvoir effectuer quelque chose comme ceci:

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

Hmm, il existe de nombreuses solutions écrites pour les versions php inférieures à 7.1.

Voici un autre simple pour ceux qui ne veulent pas intercepter toutes les exceptions et ne peuvent pas créer d'interfaces communes:

<?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.