WordPress Development Asked by Doug Wollison on January 24, 2021
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:
Some situations that I’m considering
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.
The answer by @gmazzap is quite exhaustive.
Just to add a tiny bit to it:
Consider an application with a registration form. Lets say you have a function which you use to validate the username submitted by the users.
Now the supplied username may fail validation for a number of reasons, so returning just false
isn't going to help the user much in letting them know what went wrong.
The better choice, in this case, would be to use WP_Error and to add error codes for each failed validation.
Compare this:
function validate_username( $username ) {
//No empty usernames
if ( empty( $username ) ) {
return false;
}
// Check for spaces
if ( strpos( $username, ' ' ) !== false ) {
return false;
}
// Check for length
if ( strlen( $username ) < 8 ) {
return false;
}
// Check for length
if ( ( strtolower( $username ) == $username ) || strtoupper( $username ) == $username ) ) {
return false;
}
// Everything is fine
return true;
}
with this:
function validate_username( $username ) {
//No empty usernames
if ( empty( $username ) ) {
$error->add( 'empty', 'Username can not be blank' );
}
// Check for spaces
if ( strpos( $username, ' ' ) !== false ) {
$error->add( 'spaces', 'Username can not contain spaces ' );
}
// Check for length
if ( strlen( $username ) < 8 ) {
$error->add( 'short', 'Username should be at least 8 characters long ' );
}
// Check for length
if ( ( strtolower( $username ) == $username ) || strtoupper( $username ) == $username ) ) {
$error->add( 'case', 'Username should contain a mix of uppercase and lowercase characters ' );
}
// Send the result
if ( empty( $error->get_error_codes() ) ) {
return $error;
}
// Everything is fine
return true;
}
The first version gives the user absolutely no clue about what was wrong with the username they chose. The second version adds description to errors, helping the user a lot.
Hope this helps a bit.
Answered by shariqkhan on January 24, 2021
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:
if (! is_null($thing))...
) making code harder to readI, more than often, use OOP to code plugins, and my object methods often throw exception when something goes wrong.
Doing that, I:
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 MyPluginOptions
*/
function my_plugin_get_options() {
return new MyPluginOptions('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.
Answered by gmazzap on January 24, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP