web-dev-qa-db-fra.com

Quand utiliser les exceptions vs les objets d'erreur vs tout simplement faux/null

Je suis en train d'écrire un plugin et j'essaie de déterminer quand utiliser différentes approches de gestion des erreurs.

Je considère trois méthodes:

  • Lancer une exception (classe personnalisée)
  • Retour d'un objet d'erreur (extension de WP_Error)
  • Il suffit de retourner null/false

Quelques situations que je considère

  • Essayer d'obtenir/définir une option qui n'existe pas dans le registre
  • Passer une valeur invalide à une méthode (ce qui devrait être rare)
  • Appeler une méthode que le surcharge de la classe ne peut pas résoudre

Suggestions? Étant donné que l'écriture d'un plug-in WordPress a des considérations spéciales, je ne suis pas sûr qu'il soit utile de poser cette question sur un forum PHP général.

8
Doug Wollison

Je pense qu’il est impossible de donner une réponse définitive ici, car de tels choix sont une préférence personnelle.

Considérez que ce qui suit est mon approche, et je n’ai aucune présomption que ce soit le droit un.

Ce que je peux dire, c’est que vous devriez éviter votre troisième option:

Il suffit de retourner null/false

C'est mauvais sous différents aspects:

  • consinstency de type de retour
  • rend les fonctions plus difficiles au test unitaire
  • forcer le contrôle conditionnel sur le type de retour (if (! is_null($thing))...) rendant le code plus difficile à lire

Plus que souvent, j’utilise OOP pour coder des plugins, et mes méthodes d’objet jettent souvent une exception lorsque quelque chose ne va pas.

En faisant ça, je:

  • accomplir consinstency de type de retour
  • simplifier le code en test unitaire
  • pas besoin de vérification conditionnelle sur le type retourné

Cependant, le fait de lancer des exceptions dans un plugin WordPress signifie que rien ne va les rattraper les, aboutissant à une erreur fatale qui est absolument pas souhaitable, surtout en production.

Pour éviter ce problème, j'ai normalement une "routine principale" située dans le fichier du plugin principal, que j'emballe dans un bloc try/catch. Cela me donne la chance de détecter l'exception en production et d'éviter l'erreur fatale.

Un exemple approximatif de classe:

# myplugin/src/Foo.php

namespace MyPlugin;

class Foo {

  /**
   * @return bool
   */
  public function doSomething() {
     if ( ! get_option('my_plugin_everything_ok') ) {
        throw new SomethingWentWrongException('Something went wrong.');
     }

     // stuff here...

     return true;
  }
}

et l'utiliser depuis le fichier de plugin principal:

# myplugin/main-plugin-file.php

namespace MyPlugin;

function initialize() {

   try {

       $foo = new Foo();
       $foo->doSomething();      

   } catch(SomethingWentWrongException $e) {

       // on debug is better to notice when bad things happen
       if (defined('WP_DEBUG') && WP_DEBUG) {
          throw $e;
       }

       // on production just fire an action, making exception accessible e.g. for logging
       do_action('my_plugin_error_shit_happened', $e);
   }
}

add_action('wp_loaded', 'MyPlugin\\initialize');

Bien sûr, dans le monde réel, vous pouvez lancer et capturer différents types d'exceptions et vous comporter différemment selon les exceptions, mais cela devrait vous donner une direction.

Une autre option que j'utilise souvent (et que vous n'avez pas mentionnée) consiste à renvoyer des objets contenant un indicateur afin de vérifier si aucune erreur ne se produit, tout en conservant la cohérence du type de retour.

Ceci est un exemple approximatif d'un objet comme celui-ci:

namespace MyPlugin;

class Options {

   private $options = [];
   private $ok = false;

   public function __construct($key)
   {
      $options = is_string($key) ? get_option($key) : false;
      if (is_array($options) && $options) {
         $this->options = $options;
         $this->ok = true;
      }
   }

   public function isOk()
   {
     return $this->ok;
   }
}

Maintenant, depuis n'importe quel endroit de votre plugin, vous pouvez faire:

/**
 * @return MyPlugin\Options
 */
function my_plugin_get_options() {
  return new MyPlugin\Options('my_plugin_options');
}

$options = my_plugin_get_options();
if ($options->isOk()) {
  // do stuff
}

Notez que my_plugin_get_options() ci-dessus renvoie toujours une instance de la classe Options. Ainsi, vous pouvez toujours transmettre la valeur renvoyée et même l'injecter à d'autres objets utilisant l'indication de type, ce qui vous inquiète maintenant.

Si la fonction avait retourné null/false en cas d'erreur, vous deviez vérifier si la valeur renvoyée était valide avant de la contourner.

En même temps, vous avez un moyen clair de comprendre s’il ya un problème avec l’instance d’option.

C'est une bonne solution dans le cas où l'erreur est quelque chose qui peut être facilement récupéré, en utilisant les valeurs par défaut ou ce qui convient le mieux.

5
gmazzap