There you are. Face illuminated by the blue glow of the monitor. You’ve just finished passing the third of three sets of WP_Query arguments to a function that’s supposed to run a loop with the passed args. Except…arg is what you’re feeling right now because your loop…isn’t looping. It isn’t doing anything, actually. No function my_awesome_query expected 1 parameter, none given. No fatal error on line 11123. No unexpected if (T_IF), expecting }. Nothing.
You’ll get past this. You always do. It’s probably something stupid. In fact, there it is — you’ve added some extra argument that doesn’t exist in these sets of posts you’re trying to display. You delete it and voila, it works.
This scenario should be pretty familiar. No matter what level of developer you are, at some point or another, you’ve stared at your code and said WHY ISN’T THIS WORKING??? Sometimes, a PHP fatal error saves you. But that assumes you have WP_DEBUG switched on (you do, right?). Debugging code is one of the hardest parts of writing code, and it gets exponentially harder as your plugin or theme gets bigger and does more things. Taking care to fail gracefully can not only save your bacon, it can help other people using your code understand what’s going on; remember, WordPress is open source software…someday, for some reason or another, someone may well be looking at your code and trying to decipher it. Maybe it will be you in five years, after you’ve completely forgotten about the project. Either way, leaving breadcrumbs and carefully thinking about and writing descriptive error messages is part of being a software craftsman, and it’s incredibly easy to just overlook and forget about.
But, you say, that’s what PHP errors are for. They even give me line numbers so I can trace the problem. Okay, hotshot, what about those times when even var_dumping an h1 tag that says HELLO WORLD doesn’t return anything on the page? In those cases, it can be extremely difficult to figure out what’s causing the problem, and providing a better fail option is usually trivial.
Here’s an example we should all be familiar with:
if ( have_posts() ) : while ( have_posts() ) : the_post();
If you’ve done any theme (or even plugin) development, you should know this by heart. It’s the WordPress loop. It checks for the existence of posts for a given archive page (usually the index.php or an archive template of some kind, but this can be used to display single posts or pages, too), and for each post on that page, it runs the_post() — which allows us to use template tags in that template like the_title or the_permalink. The problem is, a lot of times I come across themes that do this at the end of the loop:
endwhile; endif;
This closes out the loop, sure. But it doesn’t give you — or a user — any information about what they might be looking at. Or not looking at, as the case may be. If no posts are found, the loop just ends. It doesn’t give you any information about it. A simple alternative could be something like this:
endwhile; else : _e( 'No posts found', 'wds' ); endif;
…or even this:
endwhile; else : $labels = get_post_type_labels( get_post_type() ); $not_found = $labels->not_found; echo $not_found; endif;
…which will display the post type “no posts found” message for the current post type (read more about get_post_type_labels).
Here’s another example. You have something like this in your plugin:
function do_the_things() { if ( true == some_condition() ) { // do something } }
If your condition isn’t met, your function fails and you get no information. No PHP error, it just doesn’t work. It doesn’t take that much more effort to add an else clause:
function do_the_things() { if ( true == some_condition() ) { // do something } else { _e( 'Something went wrong. <tt>some_condition()</tt> returned false.', 'wds' ); } }
You can do the same kind of thing with a PHP switch:
function do_the_fruits() { $fruit = which_fruit(); switch( $fruit ) { case 'apples' : // do something with apples break; case 'oranges' : // do something with oranges break; case 'bananas' : // do something with bananas break; default : _e( 'Error in <tt>do_the_fruit</tt>: Could not recognize that fruit.', 'wds' ); break; } }
If your function takes a passed parameter, a good coding practice is to give it a default value. If that default value isn’t a valid option, you can return a friendlier error message telling the user (in this case, most likely a developer) what went wrong:
function which_fruit( $fruit = '' ) { if ( '' != $fruit ) { return $fruit; } else { _e( 'You did not pass a valid fruit to <tt>which_fruit</tt>.', 'wds' ); } }
In all these examples, hopefully you’ve noticed that I’m actually saying in what function the problem is happening. Just saying “I didn’t recognize that fruit” isn’t as helpful as saying “there was a problem in do_the_fruit.” Adding the function name to the message that displays makes your life a lot easier and makes it so you don’t need to go digging for answers when stuff breaks.
There’s another way to display error messages and this is built into WordPress itself. It’s the wp_die function. Now, I’m going to preface this by saying that you should use wp_die with care — wp_die kills the execution of the page, so only use it when you are sure you want a hard error message. However, if it’s between that and generating a white page of death (from a PHP fatal error), or if you are doing stuff in the WordPress admin, then it might be something worth looking into. wp_die takes 3 parameters, the message itself, the page title in the browser, and an array of additional arguments like text direction, server response code and whether to display a back button. An example might be something like this:
wp_die( '<h1>' . __( 'Uh oh! Something broke!', 'wds' ) . '</h1>', __( 'You broke it!', 'wds' ), array( 'back_link' => true ) );
Add that to your admin function and, depending on where the error is occurring in your code, you might get something like this:
…or this:
Now, obviously that’s not the best or most descriptive error message, but that shows you how important descriptive error messages are. If something breaks, just saying it’s broken or not returning any information that something broke at all isn’t helpful for debugging. Specifically saying this is what broke — this specific function — is much more helpful and is a better user experience overall than a white screen or a PHP error.
Considering what happens when a function breaks gives you a better understanding of how each piece of code fits together. As a developer, it’s easy to get into a zone and write function after function — each subsequent function hooking into the last — without stopping to consider what happens if one of those functions gives you some an unexpected response or data. It might seem like it takes longer to consider every possible scenario in your code, but having to backtrack and trawl through lines of code can take even longer than that. Next time you write an if statement in your code, consider what happens if your conditions aren’t met. Doing so may make you rethink the functionality entirely or, at the very least, help you troubleshoot problems that will inevitably come up later.
Comments