Когда использовать Исключения против Объектов Ошибок против просто false / null

8

Я нахожусь в процессе написания плагина и пытаюсь оценить, когда использовать разные подходы к обработке ошибок.

Есть три метода, которые я рассматриваю:

  • Бросать исключение (пользовательский класс)
  • Возврат объекта ошибки (расширение WP_Error)
  • Просто верните ноль / ложь

Некоторые ситуации, которые я рассматриваю

  • Попытка получить / установить параметр, хранящийся в реестре, который не существует
  • Передача недопустимого значения в метод (что должно быть редко)
  • Вызов метода, к которому перегрузчик класса не может разрешить

Предложения? Поскольку написание плагина для WordPress имеет некоторые особые соображения, я не уверен, стоит ли спрашивать об этом на обычной плате PHP.

Дуг Уоллисон
источник

Ответы:

5

Я думаю, что здесь невозможно дать однозначный ответ, потому что такой выбор является личным предпочтением.

Считайте, что то, что следует, - это мой подход, и я не думаю, что он правильный .

Что я могу сказать наверняка, так это то, что вам следует избегать третьего варианта:

Просто верните ноль / ложь

Это плохо под другим аспектом:

  • возвращаемое согласие типа
  • затрудняет юнит-тестирование функций
  • принудительная проверка типа return ( if (! is_null($thing))...) затрудняет чтение кода

Я чаще, чем часто, использую ООП для кодирования плагинов, и мои методы объекта часто выдают исключение, когда что-то идет не так.

Делая это, я:

  • выполнить возвращение типа соответствия
  • сделать код простым для модульного тестирования
  • не требуется условная проверка возвращаемого типа

Однако, бросающая исключения в плагине WordPress, означает , что ничего не будет ловить их, в конечном итоге к фатальной ошибке, абсолютно не желательно, EXPECIALLY в производстве.

Чтобы избежать этой проблемы, у меня обычно есть «основная подпрограмма», расположенная в основном файле плагина, которую я заключаю в try/ catchblock. Это дает мне возможность отловить исключение на производстве и предотвратить фатальную ошибку.

Грубый пример класса:

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

и используя его из основного файла плагина:

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

Конечно, в реальном мире вы можете бросать и ловить различные виды исключений и вести себя по-разному в соответствии с этим исключением, но это должно дать вам направление.

Другой вариант, который я часто использую (и вы не упомянули), состоит в том, чтобы возвращать объекты, содержащие флаг, чтобы проверить, не происходит ли ошибок, но сохраняя согласованность возвращаемого типа.

Это грубый пример такого объекта:

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

Теперь из любого места в вашем плагине вы можете сделать:

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

Обратите внимание, что my_plugin_get_options()выше всегда возвращает экземпляр Optionsкласса, таким образом, вы всегда можете передать возвращаемое значение и даже внедрить его в другие объекты, которые используют подсказку типа, теперь беспокоится о том, что тип отличается.

Если функция вернула null/ falseв случае ошибки, перед ее передачей вам пришлось проверить, верно ли возвращаемое значение.

В то же время у вас есть четкий способ понять, что с опцией экземпляра что-то не так.

Это хорошее решение, если ошибку легко исправить, используя значения по умолчанию или что-то еще.

Gmazzap
источник