Cuándo usar Excepciones vs Objetos de error versus simplemente falso / nulo

8

Estoy en el proceso de escribir un complemento y estoy tratando de evaluar cuándo usar diferentes enfoques para manejar errores.

Hay tres métodos que estoy considerando:

  • Lanzar una excepción (clase personalizada)
  • Devolver un objeto de error (extensión de WP_Error)
  • Solo devuelve nulo / falso

Algunas situaciones que estoy considerando

  • Intentando obtener / establecer una opción almacenada en el Registro que no existe
  • Pasar un valor no válido a un método (que debería ser raro)
  • Llamar a un método que el cargador de la clase no puede resolver

Sugerencias? Dado que escribir un complemento de WordPress tiene algunas consideraciones especiales, no estoy seguro de si valdría la pena preguntar esto en una placa PHP general.

Doug Wollison
fuente

Respuestas:

5

Creo que es imposible dar una respuesta definitiva aquí, porque opciones como esta son preferencias personales.

Considere que lo que sigue es mi enfoque, y no tengo la presunción de que sea el correcto .

Lo que puedo decir con certeza es que debe evitar su tercera opción:

Solo devuelve nulo / falso

Esto es malo en diferentes aspectos:

  • consistencia de tipo de retorno
  • hace que las funciones sean más difíciles de realizar pruebas unitarias
  • forzar la verificación condicional en el tipo de retorno ( if (! is_null($thing))...) haciendo que el código sea más difícil de leer

Yo, más que a menudo, uso OOP para codificar complementos, y mis métodos de objeto a menudo arrojan excepciones cuando algo sale mal.

Al hacer eso, yo:

  • lograr la consistencia del tipo de retorno
  • hacer el código simple para la prueba unitaria
  • no necesita verificación condicional en el tipo devuelto

Sin embargo, lanzar excepciones en un plugin para WordPress, significa que nada se coger ellos, que termina en un error fatal que es absolutamente no deseable, EXPECIALLY en la producción.

Para evitar este problema, normalmente tengo una "rutina principal" ubicada en el archivo de complemento principal, que envuelvo en un try/ catchbloque. Esto me da la oportunidad de detectar la excepción en la producción y evitar el error fatal.

Un ejemplo aproximado de una clase:

# 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;
  }
}

y usándolo desde el archivo de complemento 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');

Por supuesto, en el mundo real puede lanzar y atrapar diferentes tipos de excepción y comportarse de manera diferente según la excepción, pero esto debería darle una dirección.

Otra opción que uso a menudo (y no mencionó) es devolver objetos que contienen un indicador para verificar si no ocurre ningún error, pero manteniendo la consistencia del tipo de retorno.

Este es un ejemplo aproximado de un objeto como ese:

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;
   }
}

Ahora, desde cualquier lugar en su complemento, puede hacer:

/**
 * @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
}

Tenga en cuenta que lo my_plugin_get_options()anterior siempre devuelve una instancia de Optionsclase, de esta manera siempre puede pasar el valor de retorno e incluso inyectarlo en otros objetos que usan la sugerencia de tipo, ahora le preocupa que el tipo sea diferente.

Si la función había regresado null/ falseen caso de error, antes de pasarla se vio obligado a verificar si el valor devuelto es válido.

Al mismo tiempo, tiene una manera clara de entender si algo está mal con la instancia de la opción.

Esta es una buena solución en caso de que el error sea algo que se pueda recuperar fácilmente, utilizando valores predeterminados o lo que sea apropiado.

gmazzap
fuente