Exceptions in WordPress: About time?
Update: I’ve created a ticket on WordPress trac: https://core.trac.wordpress.org/ticket/57842. If you like this idea, then please show your support there, so we can get something like this merged into WordPress core!
I think a good introduction to error handling in WordPress would be this article from Ryan McCue from way back in 2012.
TLDR: The WP_Error
object was introduced into WordPress before PHP even had support for Exceptions!
The age of that blog post demonstrates a few things:
- Time flies!
- The
WP_Error
object is really old - WordPress has been around for a really long time
The WP_Error
object has since tied into a lot of core functionality and a lot of critical functionality like the REST API has been built on top of it.
So it looks like WP_Error
isn’t going to go away anytime soon. And it doesn’t have to for us to start using PHP Exceptions in our custom code that are not intended for public consumption – that is – until WordPress core supports it out of the box.
In the meantime, I’ve come up with the following Exception class that is meant to:
- Be compatible with
WP_Error
- Be able to convert from
WP_Error
- Be able to convert to
WP_Error
<?php
/**
* WordPress does not provide a standard Exception Class.
* Use this instead until something similar gets merged into core.
*/
namespace Junaidbhura;
use Exception;
use WP_Error;
/**
* Exception Class.
*/
class WP_Exception extends Exception {
/**
* Error code, similar to WP_Error.
*
* @var int|string Error code.
*/
protected int|string $error_code = ''; // phpcs:ignore
/**
* Error data, similar to WP_Error.
*
* @var mixed Data.
*/
protected mixed $error_data = [];
/**
* Constructor.
*
* @param int|string $error_code Error code.
* @param string $error_message Error message.
* @param mixed $error_data Error data.
*/
public function __construct( int|string $error_code = '', string $error_message = '', mixed $error_data = [] ) {
$this->error_code = $error_code;
$this->error_data = $error_data;
parent::__construct( $error_message );
}
/**
* Get error code.
*
* @return int|string
*/
public function get_error_code(): int|string {
return $this->error_code;
}
/**
* Get error message.
*
* @return string
*/
public function get_error_message(): string {
return parent::getMessage();
}
/**
* Get error data.
*
* @return mixed
*/
public function get_error_data(): mixed {
return $this->error_data;
}
/**
* Convert a WP_Error into this Exception.
*
* @param WP_Error $wp_error WP_Error object.
*
* @return $this
*/
public function from_wp_error( WP_Error $wp_error ): WP_Exception {
$this->error_code = $wp_error->get_error_code();
$this->message = $wp_error->get_error_message();
$this->error_data = $wp_error->get_error_data();
return $this;
}
/**
* Convert this exception into a WP_Error.
*
* @return WP_Error
*/
public function to_wp_error(): WP_Error {
return new WP_Error(
$this->get_error_code(),
$this->get_error_message(),
$this->get_error_data()
);
}
}
Sample usage:
use Junaidbhura\WP_Exception;
// Normal Exception.
try {
throw new WP_Exception( 'error_code', 'Error message', $error_data );
} catch ( WP_Exception $e ) {
$e->get_error_code();
$e->get_error_message();
$e->get_error_data();
}
// To WP Error.
try {
my_function();
} catch ( WP_Exception $e ) {
return $e->to_wp_error(); // Returns a WP_Error based on the exception.
}
// From WP Error.
if ( is_wp_error( $thing ) ) {
$exception = new WP_Exception();
throw $exception->from_wp_error( $thing );
}