When to use Exceptions vs Error Objects vs just plain false/null

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.

Leave a Comment