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

8

Je suis en train d'écrire un plugin et j'essaie de savoir quand utiliser différentes approches pour gérer les erreurs.

Il existe trois méthodes que j'envisage:

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

Certaines situations que j'envisage

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

Suggestions? Étant donné que l'écriture d'un plugin WordPress a des considérations particulières, je ne sais pas si cela vaut la peine de le demander sur un tableau PHP général.

Doug Wollison
la source

Réponses:

5

Je pense qu'il est impossible de donner une réponse définitive ici, car des choix comme celui-ci sont une préférence personnelle.

Considérez que ce qui suit est mon approche, et je n'ai aucune présomption que c'est la bonne .

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

Il suffit de retourner null / false

C'est mauvais sous différents aspects:

  • retour type de cohérence
  • rend les fonctions plus difficiles à tester unitairement
  • forcer une vérification conditionnelle sur le type de retour ( if (! is_null($thing))...) rendant le code plus difficile à lire

J'utilise plus que souvent la POO pour coder les plugins, et mes méthodes d'objet lèvent souvent des exceptions en cas de problème.

Ce faisant, je:

  • accomplir la cohérence du type de retour
  • rendre le code simple au test unitaire
  • pas besoin de vérification conditionnelle sur le type retourné

Cependant, lancer des exceptions dans un plugin WordPress signifie que rien ne les rattrapera , ce qui se terminera par une erreur fatale qui n'est absolument pas souhaitable, en particulier en production.

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

Un exemple grossier d'une 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 en l'utilisant à partir du fichier 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 attraper différents types d'exceptions et vous comporter différemment selon l'exception, mais cela devrait vous donner une direction.

Une autre option que j'utilise souvent (et que vous n'avez pas mentionnée) est de renvoyer des objets qui contiennent un indicateur pour vérifier si aucune erreur ne se produit, mais en gardant la cohérence du type de retour.

Voici un exemple grossier 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 comment my_plugin_get_options()ci-dessus retourne toujours une instance de Optionsclasse, de cette façon, vous pouvez toujours transmettre la valeur de retour, et même l'injecter à d'autres objets qui utilisent le type-hint avec maintenant le souci que le type soit différent.

Si la fonction était retournée null/ falseen cas d'erreur, avant de la faire circuler, vous aviez été forcé de vérifier si la valeur retournée était valide.

En même temps, vous avez clairement un moyen de comprendre si quelque chose ne va pas avec l'instance d'option.

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

gmazzap
la source