Trapping WordPress errors with output buffering.
If you have tried to use AJAX with WordPress, most likely you have stumbled upon the wp_die() function which completely kills the response you expect to get. However, we can trap the wp_die() by using PHP’s output buffering and the ob_start callback function to process the output of wp_die() even though a die() is called. This is a very special case, and will only work when you are able to ensure output buffering can be called before the die is called.
The easiest way to get a non-fatal wp_die() error (an error that shouldn’t cause the entire application to stop) is when submitting comments: non-fatal errors occur whenever someone posts too quickly or submits a duplicate comment, so this can be a problem when creating an AJAX app to submit comments (I ran into this problem with INAP.)
Since AJAX makes the entire submit process very quick, so it is easy to trigger the “Posting too quickly” error if the user make short comments, and when this happens an entire page –complete with CSS and headers– is returned as the AJAX response. Originally, I detected this by updating the element and then doing doing a regex test on it. If it was an error, I would use another Regex to strip out the error message and update the element again, but because of the CSS, if the same user triggered an error multiple times (eg testing to see if they could submit the comment yet) there would be a momentary flash. I fixed this problem by updating a variable instead of going straight to the element, but it still required using client-side code to process it.
The Setup:
Some data is posted through AJAX to the server-side script. This script then calls a function (submit_data) which then passes on the data to WordPress. (There is of course PHP and JavaScript that isn’t shown here.)
The Original Function:
function submit_form(){
global $wpdb, $post,$id;
require_once('../../../../wp-comments-post.php');
echo 'Comment submitted';
}
The original function just includes the WordPress file that processes the comments. If the Comment is a success WordPress doesn’t output any data and the “Comment submitted” message is echo’d. However if there is an error, the error message is outputted and the die() is called before the echo occurs.
The new function that traps the error:
<br />
function submit_form(){<br />
global $wpdb, $post,$id;<br />
ob_start(“nodie”);<br />
require_once(‘../../../../wp-comments-post.php’);<br />
ob_end_clean();<br />
echo ‘Comment submitted’;<br />
}</p>
<p>function nodie($error){<br />
return $error;<br />
}<br />
Now we have added the lines line 3 and line 5, plus a new function nodie(). The ob_start callback function (the string we pass to it) is only called if that specific ob_start() is supposed to output text. This only happens two times: when ob_end_flush() is called or if something inside the ob_start function outputs texts and then a die() is called. When the callback function is called the contents of the output buffer is passed as a string and a string should be returned.
In our function we use ob_end_clean() instead of ob_end_flush() which means the contents of the output buffer is destroyed, not echo’d, so the only time the call back function is called is when, you guessed it, we have an error and WordPress die()’s inside the ob_start().
Now the nodie() function doesn’t do anything, but with a little RegEX magic it will return just the error string. Because submitting comments only returns a single error we can just look for the
, but if multiple errors may be returned you may want to look for a
- also.
function nodie($error){
preg_match('@<p>(.*?)</p>@', $error,$errs);
return $errs[1];
}
A couple final notes. Inside the nodie function anything that is outputted will not be returned (eg no echo’s or print_r’s), but you can call other functions and return their output as a string, so you can create an entire error handling application. Alao, WordPress adds a header to the wp_die() page of text/html if this is inappropriate for your application you can block this by adding the following lines to your function that could cause the error.
Global $wp_actions;
$wp_actions[] = 'admin_head';
It is rather hackish, but it works. You then can set the header you need in your callback function.
Of course this technique can be used on a larger scale to completely transform the wp_die page, but in the process, it will require that you trap all content in output buffering, and this method can be used with any other PHP program that you don’t have direct control over.