8

I'm in the process of writing a plugin and I'm trying to gauge when to use different approaches of handling errors.

There are three methods I'm considering:

  • Throwing an Exception (custom class)
  • Returning an Error Object (extension of WP_Error)
  • Just return null/false

Some situations that I'm considering

  • Trying to get/set an option stored in the Registry that doesn't exist
  • Passing an invalid value to a method (which should be rare)
  • Calling a method that the class' overloader cannot resolve to

Suggestions? Since writing a WordPress plugin has some special considerations, I'm not sure whether it would be worth asking this on a general PHP board.

5

I think it's impossible to give a definitive answer here, because choices like this are personal preference.

Consider that what follows is my approach, and I have no presumption it is the right one.

What I can say for sure is that you should avoid your third option:

Just return null/false

This is bad under different aspect:

  • return type consinstency
  • makes functions harder to unit test
  • force conditional check on return type (if (! is_null($thing))...) making code harder to read

I, more than often, use OOP to code plugins, and my object methods often throw exception when something goes wrong.

Doing that, I:

  • accomplish return type consinstency
  • make the code simple to unit test
  • don't need conditional check on the returned type

However, throwing exceptions in a WordPress plugin, means that nothing will catch them, ending up in a fatal error that is absolutely not desirable, expecially in production.

To avoid this issue, I normally have a "main routine" located in main plugin file, that I wrap in a try / catch block. This gives me the chance to catch the exception in production and prevent the fatal error.

A rough example of a class:

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

and using it from main plugin file:

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

Of course, in real world you may throw and catch different kinds of exception and behave differently according to the exception, but this should give you a direction.

Another option I often use (and you don't mentioned) is to return objects that contain a flag to verify if no error happen, but keeping the return type consistency.

This is a rough example of an object like that:

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

Now, from any place in your plugin, you can do:

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

Note how my_plugin_get_options() above always returns an instance of Options class, in this way you can always pass the return value around, and even inject it to other objects that use type-hint with now worries that the type is different.

If the function had returned null / false in case of error, before passing it around you had been forced to check if returned value is valid.

At same time, you have a clearly way to understand is something is wrong with the option instance.

This is a good solution in case the error is something that can be easily recovered, using defaults or whatever fits.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.