web-dev-qa-db-fra.com

PHP: exceptions vs erreurs?

Peut-être que je le manque quelque part dans le manuel PHP, mais quelle est exactement la différence entre une erreur et une exception? La seule différence que je peux voir est que les erreurs et les exceptions sont traitées différemment. Mais qu'est-ce qui cause une exception et qu'est-ce qui cause une erreur?

108
Jason Baker

Les exceptions sont levées - elles sont destinées à être capturées. Les erreurs sont généralement irrécupérables. Disons par exemple - vous avez un bloc de code qui insérera une ligne dans une base de données. Il est possible que cet appel échoue (ID en double) - vous voudrez avoir une "erreur" qui dans ce cas est une "exception". Lorsque vous insérez ces lignes, vous pouvez faire quelque chose comme ceci

try {
  $row->insert();
  $inserted = true;
} catch (Exception $e) {
  echo "There was an error inserting the row - ".$e->getMessage();
  $inserted = false;
}

echo "Some more stuff";

L'exécution du programme se poursuivra, car vous avez "intercepté" l'exception. Une exception sera traitée comme une erreur à moins qu'elle ne soit interceptée. Il vous permettra également de poursuivre l'exécution du programme après son échec.

83
gnarf

D'habitude je set_error_handler à une fonction qui prend l'erreur et lève une exception de sorte que quoi qu'il arrive, j'aurai juste des exceptions à gérer. Pas plus @file_get_contents juste Nice et propre try/catch.

Dans les situations de débogage, j'ai également un gestionnaire d'exceptions qui génère une page similaire à asp.net. Je poste ceci sur la route mais si demandé, je posterai un exemple de source plus tard.

modifier:

Addition comme promis, j'ai coupé et collé une partie de mon code ensemble pour en faire un échantillon. J'ai enregistré le fichier ci-dessous dans un fichier sur mon poste de travail, vous pouvez PLUS DE TEMPS voir les résultats ici (car le lien est rompu).

<?php

define( 'DEBUG', true );

class ErrorOrWarningException extends Exception
{
    protected $_Context = null;
    public function getContext()
    {
        return $this->_Context;
    }
    public function setContext( $value )
    {
        $this->_Context = $value;
    }

    public function __construct( $code, $message, $file, $line, $context )
    {
        parent::__construct( $message, $code );

        $this->file = $file;
        $this->line = $line;
        $this->setContext( $context );
    }
}

/**
 * Inspire to write perfect code. everything is an exception, even minor warnings.
 **/
function error_to_exception( $code, $message, $file, $line, $context )
{
    throw new ErrorOrWarningException( $code, $message, $file, $line, $context );
}
set_error_handler( 'error_to_exception' );

function global_exception_handler( $ex )
{
    ob_start();
    dump_exception( $ex );
    $dump = ob_get_clean();
    // send email of dump to administrator?...

    // if we are in debug mode we are allowed to dump exceptions to the browser.
    if ( defined( 'DEBUG' ) && DEBUG == true )
    {
        echo $dump;
    }
    else // if we are in production we give our visitor a Nice message without all the details.
    {
        echo file_get_contents( 'static/errors/fatalexception.html' );
    }
    exit;
}

function dump_exception( Exception $ex )
{
    $file = $ex->getFile();
    $line = $ex->getLine();

    if ( file_exists( $file ) )
    {
        $lines = file( $file );
    }

?><html>
    <head>
        <title><?= $ex->getMessage(); ?></title>
        <style type="text/css">
            body {
                width : 800px;
                margin : auto;
            }

            ul.code {
                border : inset 1px;
            }
            ul.code li {
                white-space: pre ;
                list-style-type : none;
                font-family : monospace;
            }
            ul.code li.line {
                color : red;
            }

            table.trace {
                width : 100%;
                border-collapse : collapse;
                border : solid 1px black;
            }
            table.thead tr {
                background : rgb(240,240,240);
            }
            table.trace tr.odd {
                background : white;
            }
            table.trace tr.even {
                background : rgb(250,250,250);
            }
            table.trace td {
                padding : 2px 4px 2px 4px;
            }
        </style>
    </head>
    <body>
        <h1>Uncaught <?= get_class( $ex ); ?></h1>
        <h2><?= $ex->getMessage(); ?></h2>
        <p>
            An uncaught <?= get_class( $ex ); ?> was thrown on line <?= $line; ?> of file <?= basename( $file ); ?> that prevented further execution of this request.
        </p>
        <h2>Where it happened:</h2>
        <? if ( isset($lines) ) : ?>
        <code><?= $file; ?></code>
        <ul class="code">
            <? for( $i = $line - 3; $i < $line + 3; $i ++ ) : ?>
                <? if ( $i > 0 && $i < count( $lines ) ) : ?>
                    <? if ( $i == $line-1 ) : ?>
                        <li class="line"><?= str_replace( "\n", "", $lines[$i] ); ?></li>
                    <? else : ?>
                        <li><?= str_replace( "\n", "", $lines[$i] ); ?></li>
                    <? endif; ?>
                <? endif; ?>
            <? endfor; ?>
        </ul>
        <? endif; ?>

        <? if ( is_array( $ex->getTrace() ) ) : ?>
        <h2>Stack trace:</h2>
            <table class="trace">
                <thead>
                    <tr>
                        <td>File</td>
                        <td>Line</td>
                        <td>Class</td>
                        <td>Function</td>
                        <td>Arguments</td>
                    </tr>
                </thead>
                <tbody>
                <? foreach ( $ex->getTrace() as $i => $trace ) : ?>
                    <tr class="<?= $i % 2 == 0 ? 'even' : 'odd'; ?>">
                        <td><?= isset($trace[ 'file' ]) ? basename($trace[ 'file' ]) : ''; ?></td>
                        <td><?= isset($trace[ 'line' ]) ? $trace[ 'line' ] : ''; ?></td>
                        <td><?= isset($trace[ 'class' ]) ? $trace[ 'class' ] : ''; ?></td>
                        <td><?= isset($trace[ 'function' ]) ? $trace[ 'function' ] : ''; ?></td>
                        <td>
                            <? if( isset($trace[ 'args' ]) ) : ?>
                                <? foreach ( $trace[ 'args' ] as $i => $arg ) : ?>
                                    <span title="<?= var_export( $arg, true ); ?>"><?= gettype( $arg ); ?></span>
                                    <?= $i < count( $trace['args'] ) -1 ? ',' : ''; ?> 
                                <? endforeach; ?>
                            <? else : ?>
                            NULL
                            <? endif; ?>
                        </td>
                    </tr>
                <? endforeach;?>
                </tbody>
            </table>
        <? else : ?>
            <pre><?= $ex->getTraceAsString(); ?></pre>
        <? endif; ?>
    </body>
</html><? // back in php
}
set_exception_handler( 'global_exception_handler' );

class X
{
    function __construct()
    {
        trigger_error( 'Whoops!', E_USER_NOTICE );      
    }
}

$x = new X();

throw new Exception( 'Execution will never get here' );

?>
53
Kris

La réponse mérite de parler de l'éléphant dans la pièce

Erreurs est l'ancienne façon de gérer une condition d'erreur au moment de l'exécution. En règle générale, le code appelle quelque chose comme set_error_handler avant d'exécuter du code. Suivant la tradition des interruptions de langue d'assemblage. Voici à quoi ressemblerait un code BASIC.

on error :divide_error

print 1/0
print "this won't print"

:divide_error

if errcode = X
   print "divide by zero error"

Il était difficile de s'assurer que set_error_handler serait appelé avec la bonne valeur. Et pire encore, un appel pourrait être effectué vers une procédure distincte qui changerait le gestionnaire d'erreurs. De plus, de nombreux appels ont été entrecoupés de set_error_handler appels et gestionnaires. Il était facile pour le code de devenir rapidement hors de contrôle. La gestion des exceptions est venue à la rescousse en formalisant la syntaxe et la sémantique de ce que le bon code faisait vraiment.

try {
   print 1/0;
   print "this won't print";
} catch (DivideByZeroException $e) {
   print "divide by zero error";
}

Aucune fonction distincte ou risque d'appeler le mauvais gestionnaire d'erreur. Le code est maintenant garanti d'être au même endroit. De plus, nous obtenons de meilleurs messages d'erreur.

PHP n'utilisait auparavant que la gestion des erreurs, alors que de nombreux autres langages avaient déjà évolué vers le modèle de gestion des exceptions préférable. Finalement, les créateurs de PHP ont implémenté la gestion des exceptions. Mais susceptibles de prendre en charge l'ancien code, ils ont conservé la gestion des erreurs et ont fourni un moyen de faire ressembler la gestion des erreurs à la gestion des exceptions. Sauf que, il n'y a aucune garantie que certains codes peuvent ne pas réinitialiser le gestionnaire d'erreurs, ce qui était précisément ce que la gestion des exceptions était censée fournir.

Réponse finale

Les erreurs qui ont été codées avant l'implémentation de la gestion des exceptions sont probablement encore des erreurs. De nouvelles erreurs sont probablement des exceptions. Mais il n'y a pas de conception ou de logique à laquelle sont des erreurs et qui sont des exceptions. Il est simplement basé sur ce qui était disponible au moment où il a été codé et sur la préférence du programmeur qui le codait.

15
Arturo Hernandez

Une chose à ajouter ici concerne la gestion des exceptions et des erreurs. Pour le développeur de l'application, les erreurs et les exceptions sont des "mauvaises choses" que vous souhaitez enregistrer pour en savoir plus sur les problèmes rencontrés par votre application, afin que vos clients aient une meilleure expérience à long terme.

Il est donc logique d'écrire un gestionnaire d'erreurs qui fait la même chose que ce que vous faites pour les exceptions.

8
Alex Weinstein

Comme indiqué dans d'autres réponses, définir le gestionnaire d'erreurs sur le lanceur d'exceptions est le meilleur moyen de gérer les erreurs en PHP. J'utilise une configuration un peu plus simple:

set_error_handler(function ($errno, $errstr, $errfile, $errline ) {
        if (error_reporting()) {
                throw new \ErrorException($errstr, 0, $errno, $errfile, $errline);
        }
});

Veuillez noter la vérification error_reporting() pour que l'opérateur @ Continue de fonctionner. De plus, il n'est pas nécessaire de définir d'exception personnalisée, PHP a une classe Nice pour cela.

Le grand avantage de lever des exceptions est que l'exception a une trace de pile qui leur est associée, il est donc facile de trouver où est le problème.

6
Josef Kufner

Re: "mais quelle est exactement la différence entre une erreur et une exception?"

Il y a beaucoup de bonnes réponses sur les différences ici. Je vais juste ajouter quelque chose dont on n'a pas encore parlé - la performance. Plus précisément, c'est pour la différence entre lever/gérer des exceptions et gérer un code de retour (succès ou erreur). Habituellement, en php, cela signifie retourner false ou null, mais ils peuvent être plus détaillés, comme lors du téléchargement de fichiers: http://php.net/manual/en/ features.file-upload.errors.php Vous pourriez même retourner un objet Exception!

J'ai fait quelques exécutions de performance dans différents langages/systèmes. De manière générale, la gestion des exceptions est environ 10 000 fois plus lente que la recherche d'un code retour d'erreur.

Donc, si cela doit absolument, positivement terminer l'exécution avant même de commencer - eh bien, vous n'avez pas de chance car le voyage dans le temps n'existe pas. Sans voyage dans le temps, les codes retour sont l'option la plus rapide disponible.

Modifier:

PHP est hautement optimisé pour la gestion des exceptions. Des tests dans le monde réel montrent que lancer une exception n'est que 2 à 10 fois plus lent que renvoyer une valeur.

4
evan

Je pense que la réponse que vous cherchez est la suivante;

Les erreurs sont les éléments standard auxquels vous êtes habitué, comme l'écho d'une variable $ qui n'existe pas.
Les exceptions sont uniquement à partir de PHP 5 et plus et viennent quand il s'agit d'objets.

Pour faire simple:

Les exceptions sont les erreurs que vous obtenez lorsque vous traitez des objets. Cependant, l'instruction try/catch vous permet de faire quelque chose à leur sujet, et est utilisée un peu comme l'instruction if/else. Essayez de faire cela, si le problème n'a pas d'importance, faites-le.

Si vous n'attrapez pas une exception, cela se transforme en erreur standard.

Les erreurs sont les erreurs fondamentales php qui interrompent généralement votre script.

Try/catch est souvent utilisé pour établir des connexions de base de données comme PDO, ce qui est bien si vous souhaitez rediriger le script ou faire autre chose si la connexion ne fonctionne pas. Mais si vous voulez simplement afficher le message d'erreur et arrêter le script, vous n'en avez pas besoin, l'exception non interceptée se transforme en erreur fatale. Ou vous pouvez également utiliser un paramètre de gestion des erreurs à l'échelle du site.

J'espère que ça t'as aidé

4
Lan

Les exceptions sont levées intentionnellement par du code utilisant un lancer, des erreurs ... pas tellement.

Les erreurs surviennent à la suite de quelque chose qui n'est généralement pas géré. (Erreurs IO, erreurs TCP/IP, erreurs de référence nulles)

1
cgp

J'ai l'intention de vous donner une discussion très inhabituelle sur le contrôle des erreurs.

J'ai intégré un très bon gestionnaire d'erreurs dans une langue il y a des années, et bien que certains noms aient changé, les principes du traitement des erreurs sont les mêmes aujourd'hui. J'avais un système d'exploitation multitâche personnalisé et je devais être capable de récupérer des erreurs de données à tous les niveaux sans fuite de mémoire, croissance de pile ou crash. Donc, ce qui suit est ma compréhension de la façon dont les erreurs et les exceptions doivent fonctionner et comment elles diffèrent. Je dirai simplement que je ne comprends pas comment les fonctions internes de try catch fonctionnent, alors je devine dans une certaine mesure.

La première chose qui se passe sous les couvertures pour le traitement des erreurs est de passer d'un état de programme à un autre. Comment cela se fait-il? J'y arriverai.

Historiquement, les erreurs sont plus anciennes et plus simples, et les exceptions sont plus récentes et un peu plus complexes et capables. Les erreurs fonctionnent bien jusqu'à ce que vous ayez besoin de les faire bouillonner, ce qui équivaut à remettre un problème difficile à votre superviseur.

Les erreurs peuvent être des nombres, comme les numéros d'erreur, et parfois avec une ou plusieurs chaînes associées. Par exemple, si une erreur de lecture de fichier se produit, vous pourrez peut-être signaler de quoi il s'agit et éventuellement échouer avec grâce. (Hay, c'est un pas de plus que de s'écraser comme autrefois.)

Ce qui n'est pas souvent dit à propos des exceptions, c'est que les exceptions sont des objets superposés sur une pile d'exceptions spéciale. C'est comme une pile de retour pour le flux du programme, mais elle contient un état de retour juste pour les erreurs et les captures. (J'avais l'habitude de les appeler ePush et ePop, et? Abort était un lancer conditionnel qui ePop et récupérerait à ce niveau, tandis que Abort était un dé ou une sortie complète.)

Au bas de la pile se trouvent les informations sur l'appelant initial, l'objet qui connaît l'état au démarrage de l'essai externe, qui correspond souvent au démarrage de votre programme. En plus de cela, ou le calque suivant sur la pile, up étant les enfants et down étant les parents, est l'objet d'exception du prochain bloc try/catch interne.

Si vous mettez un essai à l'intérieur d'un essai, vous empilez l'essai intérieur au-dessus de l'essai extérieur. Lorsqu'une erreur se produit dans l'essai interne et que le crochet interne ne peut pas le gérer ou que l'erreur est renvoyée à l'essai externe, le contrôle est passé au bloc de capture externe (objet) pour voir s'il peut gérer l'erreur, c'est-à-dire votre superviseur.

Donc, ce que cette pile d'erreurs fait vraiment est de pouvoir marquer et restaurer le flux du programme et l'état du système, en d'autres termes, cela permet à un programme de ne pas planter la pile de retour et de gâcher les choses pour les autres (données) lorsque les choses tournent mal. Ainsi, il enregistre également l'état de toutes les autres ressources telles que les pools d'allocation de mémoire et peut donc les nettoyer une fois la capture terminée. En général, cela peut être une chose très compliquée, et c'est pourquoi la gestion des exceptions est souvent lente. En général, un peu d'état doit entrer dans ces blocs d'exception.

Donc, une sorte de bloc try/catch définit un état pour pouvoir revenir si tout le reste est gâché. C'est comme un parent. Lorsque nos vies sont gâchées, nous pouvons retomber sur les genoux de nos parents et ils vont tout remettre en ordre.

J'espère que je ne vous ai pas déçu.

1
Elliptical view

Une fois set_error_handler () défini, le gestionnaire d'erreurs est similaire à celui d'Exception. Voir le code ci-dessous:

 <?php
 function handleErrors( $e_code ) {
   echo "error code: " . $e_code . "<br>";
 }

 set_error_handler( "handleErrors" ); 

 trigger_error( "trigger a fatal error", E_USER_ERROR);
 echo "after error."; //it would run if set_error_handler is defined, otherwise, it wouldn't show
?>
0
N Zhang

Dans PHP 7.1 et versions ultérieures, un bloc catch peut spécifier plusieurs exceptions à l'aide du caractère pipe (|). Ceci est utile lorsque des exceptions différentes provenant de hiérarchies de classes différentes sont traitées. le même.

try {
  // do something
} catch (Error | Exception $e) {
  echo $e->getMessage();
}
0
Jehong Ahn