Attraper plusieurs types d'exceptions dans un seul bloc catch

244

Je voudrais un moyen plus propre pour obtenir les fonctionnalités suivantes, pour attraper AErroret BErrordans un bloc:

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

Y a-t-il un moyen de faire ça? Ou dois-je les attraper séparément?

AErroret Berroront une classe de base partagée, mais ils la partagent également avec d'autres types que j'aimerais connaître handler2, donc je ne peux pas simplement attraper la classe de base.

Dominic Gurto
la source
7
Juste pour ajouter ceci comme note latérale: Un RFC a été déposé pour intercepter plusieurs exceptions. Voyons si cette fonctionnalité est bien entrée dans le langage PHP ... wiki.php.net/rfc/multiple-catch
SimonSimCity
10
^ Cette fonctionnalité a été implémentée en PHP 7.1
Subin

Réponses:

353

Mettre à jour:

Depuis PHP 7.1, cela est disponible.

La syntaxe est:

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

Documents: https://www.php.net/manual/en/language.exceptions.php#example-287

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

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


Pour PHP avant 7.1:

Malgré ce que disent ces autres réponses, vous pouvez attraper AErroret BErrordans le même bloc (c'est un peu plus facile si vous êtes celui qui définit les exceptions). Même étant donné qu'il y a des exceptions que vous souhaitez «ignorer», vous devriez toujours être en mesure de définir une hiérarchie correspondant à 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 SPLexceptions par défaut ont une hiérarchie que vous pouvez exploiter. De plus, comme indiqué dans le manuel PHP :

Lorsqu'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 également avoir

class CError extends LetterError {}

que vous devez gérer différemment de AErrorou BError, de sorte que votre déclaration catch ressemblerait à ceci:

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

Si vous aviez le cas où il y avait au moins vingt exceptions qui appartenaient légitimement à la même superclasse et que vous deviez en gérer cinq (ou un groupe de grande taille) d'une manière et les autres de 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) {}

L'utilisation de la POO en matière d'exceptions est très puissante. Utiliser des choses comme get_classou instanceofsont des hacks, et devrait être évité si possible.

Une autre solution que j'aimerais ajouter est de mettre 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 ou les interfaces des classes d'exception (et il y en aura presque toujours un), vous pouvez effectuer les opérations suivantes:

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 façon, vous n'avez toujours qu'un seul emplacement de code que vous devez modifier si votre mécanisme de gestion des exceptions doit changer, et vous travaillez dans les constructions générales de la POO.

MirroredFate
la source
4
Voici un autre vote pour cela comme réponse correcte. Malheureusement, des choses comme ce qui est dit dans la réponse acceptée et le fait qu'elle soit acceptée comme la bonne réponse, c'est ce qui fait de PHP la folie qu'elle est.
2014
Ce devrait être la réponse acceptée. Cependant, cela suppose que vous êtes en mesure de modifier les fichiers. AErrorpourrait être implémenté dans une bibliothèque / fichier mis à jour par un tiers.
Kayla
@ WaffleStealer654 Vous pouvez toujours sous-classer les fichiers et les implémenter dans votre groupe, même si vous ne pouvez pas modifier les fichiers directement. Cela supposerait que vous pouvez lever les exceptions, mais vous pouvez simplement envelopper le mécanisme le plus basique où l'exception serait levée, puis l'attraper et lancer votre exception encapsulée.
MirroredFate
3
Ce n'est pas la réponse acceptée, car vous ne pouvez pas le faire lorsque vous utilisez une bibliothèque tierce.
Denis V
@DenisV Voir mon commentaire au-dessus du vôtre. Cela se fait tout le temps dans les logiciels d'entreprise. L'encapsulation est géniale.
MirroredFate
229

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


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

Si vous ne le pouvez pas, vous pouvez essayer de tout attraper avec Exception, puis vérifier avec quelle exception a été levée 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 d' utiliser plusieurs blocs catch comme décrit dans la réponse susmentionnée .

try
{
    /* something */
}
catch( AError $e )
{
   handler1( $e );
}
catch ( BError $b )
{
   handler2( $e );
}
alex
la source
6
Voilà ce que j'avais peur. Les attraper ensemble et tester le type serait bien s'il y avait beaucoup de types d'erreur qui devaient être traités ensemble, mais pour seulement 2, comme dans mon cas, les attraper séparément est probablement plus propre. Merci!
Dominic Gurto
3
@DominicGurto: Ouais, j'irais avec ça aussi :) Je serais plus préoccupé par l'attitude de PHP envers une finallydéclaration. ;)
alex
7
Mais n'oubliez pas que cela intercepte TOUTES les exceptions, donc il devrait y avoir quelque chose comme ... } else { throw($e); }s'il ne correspond pas aux deux. Désolé pour la syntaxe peut-être erronée, n'a pas vu php depuis un moment.
Dalibor Filus
11
Si vous lisez le premier paragraphe ici: php.net/manual/en/language.exceptions.php, vous verrez que plusieurs blocs catch sont une solution possible et parfaitement valide. L'OP avait cependant mis par erreur deux classes d'exception dans une déclaration de capture. Je pense qu'il sera préférable de mettre à jour votre réponse avec un autre exemple avec plusieurs blocs catch.
Haralan Dobrev
4
Suggérer une solution qui mange toutes vos autres exceptions, n'aurait pas dû être accepté du tout ...
Stivni
88

Venir en PHP 7.1 est la possibilité d'attraper plusieurs types.

Pour que ceci:

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

Joe Watkins
la source
45

Depuis PHP 7.1,

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

intéressant, vous pouvez également:

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

et dans les versions antérieures 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
    }
}
hanshenrik
la source
25

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

Exemples d'exceptions

Voici quelques exemples d'exceptions qui ont été définis pour les besoins 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
  }
}

Gérer plusieurs exceptions

C'est très simple - il peut y avoir un bloc catch pour chaque type d'exception qui peut ê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 levée qui n'est gérée par aucune des autres instructions catch, elle sera gérée par le bloc catch (Exception $ e). Ce n'est pas nécessairement le dernier.

user1983902
la source
3
Le problème avec cette méthode survient lorsque vous devez exécuter le même code pour deux ou plusieurs exceptions différentes.
Parziphal
Cela a été récupéré de Electric Toolbox . Modification du post pour donner du crédit.
Kayla
Avec PHP 7.x, vous devez catch (Throwable $e)intercepter toutes les exceptions. Voir aussi: php.net/manual/en/class.throwable.php
Mikko Rantalainen
21

En tant qu'extension de la réponse acceptée, vous pouvez changer le type d'exception, ce qui donne un modèle qui ressemble un peu à 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;

    }

}
smix96
la source
6
utilisez plusieurs captures au lieu de cette solution.
Alejandro Moreno
5

Voici une alternative raisonnable si vous ne contrôlez pas la définition des exceptions. Utilisez le nom de la variable d'exception pour classer les exceptions lorsqu'elles sont interceptées. Vérifiez ensuite 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 ne vaut probablement la peine que s'il y a beaucoup de duplication entre les implémentations de blocs catch.

dellsala
la source
3

Outre la fonction Fall-Through, il est également possible de passer à l'étape suivante à l'aide de 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";
}

3v4l.org

ml_
la source
1

Un excellent moyen est d'utiliser set_exception_handler.

Avertissement!!! 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 nullet vous vous attendez à voir cela si le rapport d'erreurs est activé.

L'erreur ci-dessus ne sera PAS détectée catch(Exception $e). L'erreur ci-dessus ne déclenchera PAS de gestionnaire d'erreur personnalisé défini par set_error_handler.

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

class ErrorHandler{
    public static function excep_handler($e)
    {
        print_r($e);
    }
}
set_exception_handler(array('ErrorHandler','excep_handler'));
Frank Forte
la source
1
... ou vous pourriez simplement écrire catch (Throwable $e) { ... }et en finir. Voir aussi: php.net/manual/en/class.throwable.php
Mikko Rantalainen
0

Une autre option non répertoriée ici consiste à utiliser l' codeattribut d'une exception, vous pouvez donc faire 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
    }
}
Mike Purcell
la source
Je n'ai pas déçu, mais peut-être que les puristes de la POO sont en colère que vous n'ayez pas créé de nouvelles classes d'exception à l'aide extends \Exception?
keyboardSmasher
Je l'ai. C'est tout l'intérêt de ma solution, vous n'avez pas besoin de créer des classes arbitraires juste pour établir un espace de noms pour lever une exception spécifique. Je suis certain que c'est pourquoi ils ont ajouté la possibilité de spécifier un code.
Mike Purcell
Je n'ai pas non plus déçu, mais je suppose que les dépréciants croient que cela ne répond pas à la question. Je suggère de commencer la réponse par quelque chose qui indique clairement au lecteur que vous avez compris la question et que vous souhaitez toujours suggérer une manière totalement différente pour le flux de code. Cette réponse ne répond vraiment pas "comment intercepter plusieurs types d' exceptions " mais plutôt "comment gérer plusieurs causes différentes pour une exception".
Mikko Rantalainen
0

Hmm, il existe de nombreuses solutions écrites pour la version php inférieure à 7.1.

Voici un autre simple pour ceux qui ne veulent pas attraper tous 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!
}
?>
GT.
la source