Gestion des erreurs en PHP lors de l'utilisation de MVC


12

J'ai utilisé Codeigniter beaucoup récemment, mais une chose qui m'énerve est de gérer les erreurs et de les afficher à l'utilisateur. Je n'ai jamais été bon dans la gestion des erreurs sans que cela devienne salissant. Ma principale préoccupation est de renvoyer des erreurs à l'utilisateur.

Est-ce une bonne pratique d'utiliser des exceptions et de lever / intercepter des exceptions plutôt que de renvoyer 0 ou 1 à partir de fonctions, puis d'utiliser if / else pour gérer les erreurs. Ainsi, il est plus facile d'informer l'utilisateur du problème.

J'ai tendance à m'éloigner des exceptions. Il y a quelques années, mon tuteur Java à l'université m'a dit "les exceptions ne devraient pas être utilisées dans le code de production, c'est plus pour le débogage". J'ai l'impression qu'il mentait.

Mais, par exemple, j'ai du code qui ajoute un utilisateur à une base de données. Au cours du processus, plus d'une chose peut mal tourner, comme un problème de base de données, une entrée en double, un problème de serveur, etc. Lorsqu'un problème survient lors de l'inscription, l'utilisateur doit le savoir.

Quelle est la meilleure façon de gérer les erreurs en PHP, en gardant à l'esprit que j'utilise un framework MVC.

Réponses:


14

Est-ce une bonne pratique d'utiliser des exceptions et de lever / intercepter des exceptions plutôt que de renvoyer 0 ou 1 à partir de fonctions, puis d'utiliser if / else pour gérer les erreurs. Ainsi, il est plus facile d'informer l'utilisateur du problème.

Non non Non!

Ne mélangez pas les exceptions et les erreurs. Les exceptions sont, bien, exceptionnelles. Les erreurs ne le sont pas. Lorsque vous demandez à un utilisateur d'entrer une quantité d'un produit et que l'utilisateur entre "bonjour", c'est une erreur. Ce n'est pas une exception: il n'y a rien d'exceptionnel à voir une entrée invalide de l'utilisateur. Pourquoi ne pouvez-vous pas utiliser des exceptions dans des cas non exceptionnels, comme lors de la validation d'une entrée? D'autres personnes l'ont déjà expliqué et ont montré une alternative valable pour la validation des entrées.

Cela signifie également que l'utilisateur ne se soucie pas de vos exceptions , et montrer les exceptions est à la fois hostile et dangereux . Par exemple, une exception lors de l'exécution d'une requête SQL révèle souvent la requête elle-même. Êtes-vous sûr de vouloir prendre le risque de montrer un tel message à tout le monde?

plus d'une chose pourrait mal tourner, comme un problème de base de données, une entrée en double, un problème de serveur, etc. Lorsqu'un problème survient lors de l'inscription, l'utilisateur doit le savoir.

Faux. En tant qu'utilisateur, je n'ai pas besoin de connaître vos problèmes de base de données, les entrées en double, etc. Je ne me soucie vraiment pas de vos problèmes. Ce que je fais besoin de savoir est que je suis entré dans un nom d' utilisateur qui existe déjà. Comme déjà dit, une mauvaise entrée de ma part doit déclencher une erreur, pas une exception.

Comment sortir ces erreurs? Ça dépend du contexte. Pour un nom d'utilisateur déjà utilisé, je voudrais voir un petit drapeau rouge apparaître près du nom d'utilisateur, avant même de soumettre le formulaire, disant que le nom d'utilisateur est déjà utilisé. Sans JavaScript, le même drapeau doit apparaître après la soumission.

Un exemple d'une erreur compatible AJAX

Pour les autres erreurs, vous afficheriez une page entière avec une erreur, ou choisiriez une autre manière d'informer l'utilisateur que quelque chose s'est mal passé (par exemple un message qui apparaîtra, puis disparaîtra en haut de la page). La question est alors plus liée à l'expérience utilisateur qu'à la programmation.

Du point de vue des programmeurs, selon le type de l'erreur, vous la propagerez de différentes manières. Par exemple, dans le cas d'un nom d'utilisateur déjà pris, une demande AJAX pour http://example.com/?ajax=1&user-exists=Johnretourner un objet JSON indiquant:

  • Que l'utilisateur existe déjà,
  • Le message d'erreur à afficher à l'utilisateur.

Le deuxième point est important: vous voulez vous assurer que le même message apparaît à la fois lors de la soumission du formulaire avec JavaScript désactivé et de la saisie d'un nom d'utilisateur en double avec JavaScript activé. Vous ne voulez pas dupliquer le texte du message d'erreur dans le code source côté serveur et en JavaScript!

Il s'agit en fait de la technique utilisée par les sites Web Stack Exhange. Par exemple, si j'essaie de voter pour ma propre réponse, la réponse AJAX contient l'erreur à afficher:

{"Success":false,"Warning":false,"NewScore":0,"Message":"You can't vote for your own post.",
"Refresh":false}

Vous pouvez également choisir une autre approche et prédéfinir les erreurs dans la page HTML avant de remplir le formulaire. Avantages: vous n'avez pas à envoyer le message d'erreur dans la réponse AJAX. Inconvénients: qu'en est-il de l'accessibilité? Essayez de parcourir la page sans CSS, et vous verrez toutes les erreurs possibles apparaître.


J'apprécie la réponse. C'est exactement ce avec quoi je me bats. Avez-vous des ressources pour signaler des erreurs, notamment en termes d'expérience utilisateur?
James Jeffery

Eh bien, comme je l'ai dit, cela dépend vraiment de l'erreur, et le signalement des erreurs à l'utilisateur est fortement lié à l'interface utilisateur. J'ai également mis en évidence deux façons principales de signaler les erreurs: une intégration étroite (un drapeau rouge compatible AJAX près de l'entrée avec une valeur incorrecte) et des erreurs de pleine page, beaucoup moins conviviales, utilisées pour les cas plus graves. Cela ne répond-il pas à votre question?
Arseni Mourzenko

2
+1 car il s'agit plus d'un problème d'expérience utilisateur que d'un problème technique
Charles Sprayberry

2
Conneries, MainMa. Juste des conneries. Les codes d'erreur sont donc des années 80 et 90. Les exceptions sont un moyen beaucoup plus propre de gérer des circonstances spéciales comme une mauvaise entrée (ValidationException par exemple). Vous n'êtes pas obligé d'afficher toutes les exceptions à l'utilisateur. J'ai vu de meilleures réponses de la vôtre.
Falcon

2
Et juste au cas où vous ne le saviez pas: vous pouvez contrôler les exceptions que vous souhaitez présenter à l'utilisateur et celles que vous ne souhaitez pas présenter. Ce n'est donc pas du tout un argument.
Falcon

13

Est-ce une bonne pratique d'utiliser des exceptions et de lever / intercepter des exceptions plutôt que de renvoyer 0 ou 1 à partir de fonctions, puis d'utiliser if / else pour gérer les erreurs. Ainsi, il est plus facile d'informer l'utilisateur du problème.

Oui oui oui!

Si vous voulez avoir un code propre, vous devez presque exclusivement utiliser des exceptions et ne pas vous embêter à utiliser des codes d'erreur. Les codes d'erreur n'ont aucun sens. Ils sont presque toujours liés à une constante numérique qui ne révèle pas beaucoup d'informations. Cela peut rendre votre code illisible et il sera difficile de propager les données à côté de l'erreur.

Les exceptions, cependant, sont des classes et peuvent contenir toute information que vous aimez. L'utilisateur a donc entré une mauvaise entrée, comme «abc» pour un champ numérique. Avec un code d'erreur, vous ne pourriez pas propager ces informations au gestionnaire de l'erreur sans beaucoup de bulles. Quelque chose que les exceptions fournissent gratuitement. De plus, les exceptions vous permettent d'avoir des valeurs de retour significatives dans les fonctions et les méthodes tout en ayant un moyen d'échouer élégamment. Encore mieux, les exceptions se propagent directement à l'endroit où vous souhaitez les gérer! Imaginez la quantité de code spaghetti dont vous aurez besoin pour propager un code d'erreur avec des données significatives à un gestionnaire une ou deux couches au-dessus.

De plus, les exceptions s'expriment beaucoup plus sémantiquement que les codes d'erreur. Les codes d'erreur conduisent à du code spaghetti où la gestion des exceptions conduit à un code propre.

De plus, il est facile d'oublier de vérifier les codes d'état. Dans des langages comme Java, vous êtes obligé de gérer les exceptions (quelque chose qui, par exemple, C # manque).

Quelle est la meilleure façon de gérer les erreurs en PHP, en gardant à l'esprit que j'utilise un framework MVC.

Utilisez des exceptions et gérez-les dans vos contrôleurs.


Je suis bien d'accord avec toi !! Bien que les exceptions ne soient pas aussi appliquées en PHP que dans d'autres langages, il est bon de savoir que beaucoup de gens les intègrent ...
David Conde

6
Je suis d'accord que dans la plupart des cas, les codes d'erreur sont assez dénués de sens. Cependant, lever des exceptions à volonté est extrêmement mauvais! Les exceptions ne devraient être réservées qu'à des circonstances exceptionnelles. Les exceptions entraînent un flux de programme imprévisible, peuvent rendre le code difficile à suivre (et donc à maintenir), et en PHP, elles entraînent une pénalité de performance plutôt importante par rapport à IF / THEN / ELSE. J'ai tendance à préférer les méthodes pour retourner vrai en cas de succès, faux en cas d'échec, et ne lever qu'une exception de quelque chose qui va vraiment de travers.
GordonM

6

Considérez cette petite classe pratique:

class FunkyFile {               

    private $path;
    private $contents = null;

    public function __construct($path) { 
        $this->setPath($path); 
    }

    private function setPath($path) {
        if( !is_file($path) || !is_readable($path) ) 
            throw new \InvalidArgumentException("Hm, that's not a valid file!");

        $this->path = realpath($path);
        return $this; 
    }

    public function getContents() {
        if( is_null($this->contents) ) {
            $this->contents = @file_get_contents( $this->path );
            if($this->contents === false) 
                throw new \Exception("Hm, I can't read the file, for some reason!");                                 
        }

        return $this->contents;            
    }

}

C'est une utilisation parfaitement fine des exceptions. Du FunkyFile'spoint de vue, il n'y a absolument rien qui puisse être fait pour remédier à la situation si le chemin est invalide ou file_get_contentséchoue. Une situation vraiment exceptionnelle;)

Mais est-il utile pour votre utilisateur de savoir que vous êtes tombé sur un mauvais chemin de fichier, quelque part dans votre code? Par exemple:

class Welcome extends Controller {

    public function index() {

        /**
         * Ah, let's show user this file she asked for
         */                 
        try {
            $file = new File("HelloWorld.txt");
            $contents = $file->getContents();   
            echo $contents;
        } catch(\Exception $e) {
            log($e->getMessage());

            echo "Sorry, I'm having a bad day!"; 
        }                           
    }        
}

En plus de dire aux gens que vous passez une mauvaise journée, vos options sont les suivantes:

  1. Se retirer

    Avez-vous un autre moyen d'obtenir les informations? Dans mon exemple simple ci-dessus, cela ne semble pas probable, mais considérons un schéma de base de données maître / esclave. Le maître n'a peut-être pas répondu, mais peut-être, juste peut-être, l'esclave est toujours là (ou vice versa).

  2. Est-ce la faute de l'utilisateur?

    L'utilisateur a-t-il soumis une entrée défectueuse? Eh bien, parlez-en. Vous pouvez soit aboyer un message d'erreur, soit être gentil et accompagner ce message d'erreur avec un formulaire afin qu'elle puisse taper le bon chemin.

  3. C'est de ta faute?

    Et par vous, je veux dire tout ce qui n'est pas l'utilisateur, ce qui va de vous à taper un mauvais chemin de fichier, à quelque chose qui va de travers dans votre serveur. À proprement parler, il est temps pour une erreur HTTP 503 , car le service n'est pas disponible. CI a une show_404()fonction, vous pouvez facilement construire show_503().

Un conseil, vous devez prendre en considération les exceptions voyous. CodeIgniter est un morceau de code en désordre, et vous ne savez jamais quand une exception apparaîtra. De même, vous pouvez oublier vos propres exceptions, et l'option la plus sûre est d'implémenter un gestionnaire d'exceptions catch all. En PHP, vous pouvez le faire avec set_exception_handler :

function FunkyExceptionHandler($exception) {
    if(ENVIRONMENT == "production") {
        log($e->getMessage());
        show_503();
    } else {
        echo "Uncaught exception: " , $exception->getMessage(), "\n";
    }   
}

set_exception_handler("FunkyExceptionHandler");

Et vous pouvez également vous occuper des erreurs non fiables , via set_error_handler . Vous pouvez soit écrire le même gestionnaire que pour les exceptions, soit convertir toutes les erreurs en ErrorExceptionet laisser votre gestionnaire d'exceptions les traiter:

function FunkyErrorHandler($errno, $errstr, $errfile, $errline) {
    // will be caught by FunkyExceptionHandler if not handled
    throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
}

set_error_handler("FunkyErrorHandler");

C'était vraiment instructif, bravo!
James
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.