Quando usar exceções x objetos de erro x simplesmente falso / nulo

8

Estou escrevendo um plug-in e estou tentando avaliar quando usar diferentes abordagens de manipulação de erros.

Existem três métodos que estou considerando:

  • Lançando uma exceção (classe personalizada)
  • Retornando um objeto de erro (extensão de WP_Error)
  • Basta retornar nulo / falso

Algumas situações que estou considerando

  • Tentando obter / definir uma opção armazenada no Registro que não existe
  • Passando um valor inválido para um método (o que deve ser raro)
  • Chamar um método que o sobrecarregador da classe não pode resolver para

Sugestões? Como escrever um plug-in do WordPress tem algumas considerações especiais, não tenho certeza se valeria a pena perguntar isso em um quadro geral do PHP.

Doug Wollison
fonte

Respostas:

5

Eu acho que é impossível dar uma resposta definitiva aqui, porque escolhas como essa são preferência pessoal.

Considere que o que se segue é a minha abordagem, e não tenho presunção de que seja a correta .

O que posso dizer com certeza é que você deve evitar sua terceira opção:

Basta retornar nulo / falso

Isso é ruim sob aspectos diferentes:

  • tipo de retorno consinstency
  • torna as funções mais difíceis de testar na unidade
  • forçar verificação condicional no tipo de retorno ( if (! is_null($thing))...), dificultando a leitura do código

Eu, mais frequentemente, uso OOP para codificar plug-ins, e meus métodos de objeto geralmente lançam exceção quando algo dá errado.

Fazendo isso, eu:

  • realizar consinstência de tipo de retorno
  • simplifique o código para o teste de unidade
  • não precisa de verificação condicional no tipo retornado

No entanto, lançar exceções em um plugin do WordPress significa que nada as pegará , resultando em um erro fatal que não é absolutamente desejável, especialmente na produção.

Para evitar esse problema, normalmente tenho uma "rotina principal" localizada no arquivo principal do plug-in, que envolvo em um bloco try/ catch. Isso me dá a chance de capturar a exceção na produção e evitar o erro fatal.

Um exemplo aproximado de uma 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;
  }
}

e usando-o no arquivo principal do plug-in:

# 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');

Obviamente, no mundo real, você pode lançar e capturar diferentes tipos de exceção e se comportar de maneira diferente de acordo com a exceção, mas isso deve lhe dar uma direção.

Outra opção que costumo usar (e você não mencionou) é retornar objetos que contêm um sinalizador para verificar se nenhum erro ocorre, mas mantendo a consistência do tipo de retorno.

Este é um exemplo aproximado de um objeto como esse:

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

Agora, de qualquer lugar no seu plugin, você pode:

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

Observe como my_plugin_get_options()acima sempre retorna uma instância da Optionsclasse; dessa forma, você sempre pode passar o valor de retorno e até injetá-lo em outros objetos que usam a dica de tipo, agora preocupam que o tipo seja diferente.

Se a função retornou null/ falseem caso de erro, antes de passá-la, você foi forçado a verificar se o valor retornado é válido.

Ao mesmo tempo, você tem uma maneira clara de entender se algo está errado com a instância da opção.

Essa é uma boa solução, caso o erro seja algo que possa ser facilmente recuperado, usando padrões ou o que for adequado.

gmazzap
fonte