Key Takeaways
- There is no single “WordPress Way” to handle and display errors from the save_post hook in WordPress, but there are several methods that can be used, each with their pros and cons.
- The first method is to save the error in the $_SESSION, which is fairly easy to implement and doesn’t require hitting the database. However, it is not the “WordPress Way” and could cause problems for some users who don’t have a session store.
- The second method is to save the error in a transient, which is a WordPress-friendly way of managing errors and ensures the data will be cleared out on its own. However, it has to hit the database if you don’t have an object cache installed, which may not be ideal if you’re doing a lot of work in the save_post hook.
- The third method is to add a GET param to the redirect, which doesn’t require hitting the database but requires duplicating the messages in both the instantiation of the WP_Error object and the switch statement. This could be more difficult to maintain long-term if your application gets large.
Despite the existence of WP_Error
and all the pieces required, there is no single “WordPress Way” to handle and display errors. You can indicate an error occurred in a function or method by returning the WP_Error
object (and that’s been my usual MO in my work), but once we do, how do we handle these error objects?
It’s easy on the AJAX side of things: Use wp_send_json_{error/success}
and handle the response accordingly. However, a common area where generated errors need to be displayed to the user is on the save_post
hook, which is actually slightly more complicated than it looks.
The reason for this is due to the way WordPress handles saves. If you’re on a page like post.php?post=1234
and you make your edits and hit save, WordPress POST
’s the information to post.php
. After the save_post
hook fires, everything that’s done is done, it then redirects back to the post.php?post=1234
editor page.
This makes things difficult because the redirect means the execution thread for loading the editor page isn’t the same as the thread for saving the post, and we no longer have access to the same global variables we did when we were saving. Because of this, we need a way to pass that information from the action to the page we’re being redirected to.
Let’s walk through 3 potential methods of solving this problem. I’ll explain how to implement each one, point out some of their pros and cons, and explain to which contexts they’re best suited.
Reference Boilerplate
Before we get started, I’m going to be using the admin_notices
hook for error displaying, which looks like this:
add_action( 'admin_notices', 'my_error_message' );
This hook gives us a place to display messages above the page title. I’ll detail the my_error_message
function for each method as we go. I’m also going to ignore whatever you decide to do in the save_post
hook and just assume you end up at the end of the hook with an $error
, which is either false
if there were no errors or a WP_Error
object if there was. The execution flow would look something like this:
add_action( 'save_post', 'my_save_post_function' );
function my_save_function( $post_id ) {
$error = false;
// Do stuff.
if ($something_went_wrong) {
$error = new WP_Error($code, $msg);
}
if ($error) {
// Handle error.
}
return true;
}
Something happens with the $post_id
(not pictured), and if something goes wrong, we’ll create a WP_Error
object which we’ll handle.
Save It in the $_SESSION
PHP (and WordPress, actually) is (in)famous for its globals. Similar to how the $_GET
and $_POST
variables hold the data for those HTTP verbs, the $_SESSION
global holds the data for the current session. The $_SESSION
data is persisted in the configured session storage (e.g. memcache, Redis, filesystem) and tied to the user’s session through a cookie. We can use that to save and retrieve our error message.
How It Works
First, you’ll have to initiate the session, as WordPress doesn’t use sessions on its own:
if ( !session_id() ) {
session_start();
}
This should be hooked early in the WordPress lifecycle, although I think you could start the session in the save_post
hook itself if you’d like. Then, we save the error to the session:
if ($error) {
$_SESSION['my_plugin_errors'] = $error->get_error_message();
}
Finally, in the admin_notices
hook:
if ( array_key_exists( 'my_plugin_errors', $_SESSION ) ) {?>
<div class="error">
<p><?php echo $_SESSION['my_plugin_errors']; ?></p>
</div><?php
unset( $_SESSION['my_plugin_errors'] );
}
we pull the error message from the session and output it above the page title.
Pros and Cons
On the plus side, this solution is fairly easy to implement and doesn’t require you to hit the database, which the other two solutions do. The major problem with it is: WordPress doesn’t use sessions. It’s designed to be “stateless,” so it’s not designed to maintain information as it proceeds through its operations, which will cause problems for some users if they don’t have a session store. Fundamentally, it’s not “The WordPress Way” and runs counter to the philosophy of the project, but it is an easy and lightweight solution to the problem if you’re building a tool for yourself or a client, where you have full control over the environment.
Saving It in a Transient
Transients are a WordPress caching API. It allows you to save any kind of information with an expiration date, like you would in any other cache. Under the hood, the Transient API will attempt to save your data to the object cache, if you have a persistent cache enabled. If you don’t, it will save your data to the wp_options
table. This way, no matter how the site’s back-end is configured there’s a (somewhat) reliable API for persisting short-term data. It abstracts a lot of hard work for you.
One word of caution: While the data should be there (and for the short period of time we’re saving it for, it almost always will be) when we get it, there are reasons it could get lost, especially of it’s being saved to the object cache. You can’t assume the data will always be there. If you absolutely need the information to persist, you should save it to the database directly.
How It Works
We don’t need to initiate anything to get started with transients. When you get an error, just set a transient with the information (you’ll need to fetch the $user_id
in advance):
if ($error) {
set_transient("my_save_post_errors_{$post_id}_{$user_id}", $error, 45);
}
Here’s the reference for that function. Just note that the last param is the number of seconds that the transient will last for. 45 seconds should be enough time to save and get the data, and if it isn’t, something has gone horribly wrong.
Then, in the admin_notices
hook:
if ( $error = get_transient( "my_save_post_errors_{$post_id}_{$user_id}" ) ) { ?>
<div class="error">
<p><?php echo $error->get_error_message(); ?></p>
</div><?php
delete_transient("my_save_post_errors_{$post_id}_{$user_id}");
}
we grab the WP_Error
from the cache and display its message. Don’t forget to clean up after yourself!
Pros and Cons
On the plus side, this is a WordPress-friendly way of managing these errors, designed for caching stuff like this. The data will be cleared out on its own, even if you forget to clear it yourself, so it provides a kind of sanity check for you, wiping or ignoring stale error messages. The primary problem with this solution is it has to hit the database if you don’t have an object cache installed, so if you’re doing a lot of work in the save_post
hook and don’t want to add another query, this may not be the ideal solution.
I’ll also mention you can use this same idea with post_meta
. If I recall correctly, this may not add an extra query, as it just adds a statement to the query for the post, but I’m not sure. Using post_meta
for temporary data isn’t really it’s ideal purpose, but if it gives you a performance boost, it may be worth it. Definitely make sure to clean up after yourself then, since that won’t be done for you with post_meta
the way transients
do.
Adding a GET
Param to the Redirect
This third method is actually the way WordPress shows its “Post Updated” message: with a $_GET
param. In WordPress’s instance, it sets the message
value to a number, and uses that number to display a particular message. You can do something similar, adding the WP_Error
’s code, instead of its full message, to the URL as a query variable.
At the end of our save_post
hook, we add the error code to the URL parameter:
if ($error) {
add_filter('redirect_post_location', function( $location ) use ( $error ) {
return add_query_arg( 'my-plugin-error', $error->get_error_code(), $location );
});
}
Anonymous functions weren’t ADDED until PHP 5.3, and WordPress supports 5.2+, so their use here may disqualify you from this method, but this is a pretty ideal use case for them, pulling in a variable from its context and using it in the filter.
Then, we display the error message, based on its code, in the admin_notices hook:
if ( array_key_exists( 'my-plugin-error', $_GET) ) { ?>
<div class="error">
<p>
<?php
switch($_GET['my-plugin-error']) {
case 'an_error_code':
echo 'The post failed to save because problems.';
break;
case 'another_error_code':
echo 'The post failed to save because reasons.';
break;
default:
echo 'An error ocurred when saving the post.';
break;
}
?>
</p>
</div><?php
}
Pros and Cons
The big plus on this is you never have to hit the database. All of the error codes are stored and retrieved in memory, which makes it much more performant than the others. The negative side is you have to duplicate the messages in both the instantiation of the WP_Error
object and the switch statement. You can’t use a constant or a set of variables or anything like that because then the strings can’t be translated. If you have a limited number of error codes, this shouldn’t be an issue, but if your application gets large, it could be more difficult to maintain long-term.
Conclusion
Each of these three methods are better or easier to implement in various situations. I like to use transients to pass data around across page loads, especially when displaying error messages the user needs to see and possibly react to. The query variable has the lowest overhead of all three, and works great when your application only generates a small number of errors. And while the session isn’t really supported by WordPress, it is a common method for flashing messages to the user in other applications.
I hope that these potential approaches to displaying errors have been useful to you. Leave me a comment if you have any questions.
Frequently Asked Questions (FAQs) about Displaying Errors from the Save Post Hook in WordPress
What is the save_post hook in WordPress?
The save_post hook in WordPress is a type of action hook that gets triggered whenever a post is created or updated. It allows developers to execute custom code whenever these events occur. This can be particularly useful for tasks such as custom validation, metadata updates, or triggering other events in response to a post being saved.
How can I use the save_post hook in my WordPress theme?
To use the save_post hook in your WordPress theme, you need to add a function to your theme’s functions.php file. This function should contain the custom code you want to execute when a post is saved. You then use the add_action function to attach your custom function to the save_post hook.
Why am I not seeing any errors from the save_post hook?
If you’re not seeing any errors from the save_post hook, it could be due to a few reasons. Firstly, ensure that your custom function is correctly attached to the save_post hook. Secondly, make sure that your function is correctly written and doesn’t contain any syntax errors. Lastly, check that your function is actually triggering an error. If your function runs successfully, it won’t produce any errors.
How can I display errors from the save_post hook?
To display errors from the save_post hook, you can use the add_settings_error function. This function allows you to add a settings error to the queue which can then be displayed on the admin page. You can also use the settings_errors function to display these errors.
Can I use the save_post hook with custom post types?
Yes, you can use the save_post hook with custom post types. The save_post hook is triggered whenever any post type is saved, including custom post types. You can specify the post type in your add_action function to ensure your custom function only runs for that specific post type.
How can I prevent my custom function from running during auto-saves?
To prevent your custom function from running during auto-saves, you can use the DOING_AUTOSAVE constant. This constant is set to true during an auto-save. You can check for this constant in your custom function and return early if it’s set to true.
Can I use the save_post hook to update post metadata?
Yes, you can use the save_post hook to update post metadata. You can use the update_post_meta function within your custom function to update the metadata for the saved post.
How can I debug my save_post hook function?
To debug your save_post hook function, you can use various debugging tools available for WordPress. These include the WP_DEBUG constant, the Debug Bar plugin, and the Query Monitor plugin. These tools can help you identify any errors or issues in your custom function.
Can I use the save_post hook to trigger other hooks?
Yes, you can use the save_post hook to trigger other hooks. You can do this by calling do_action within your custom function. This allows you to trigger other actions in response to a post being saved.
How can I remove my custom function from the save_post hook?
To remove your custom function from the save_post hook, you can use the remove_action function. This function allows you to detach your custom function from the save_post hook, preventing it from running when a post is saved.
James DiGioia is a front- and back-end developer working in JavaScript and PHP with experience in ecommerce applications.