Here’s my presentation slides from WordCamp NYC 2012. Click on the slide below to go full-screen and arrow keys to navigate.
Debugging
The end goal of debugging is to find the causes to cute (and not-so-cute) bugs so we can squish them.
Who am I?
- Mohammad (Mo) Jangda
- Toronto, ON
- Code Wrangler, WordPress.com VIP at Automattic
- Ice Cream fan
- batmoo (.org)
- @mjangda
When you debug, you work similar to a doctor or an investigator.
On a really high-level
- Determine the problem
- Gather information
- Analyze the symptoms
- Rule out improbable causes
- Make educated or exact guesses about the root cause(s)
- …and then fix the problem
Knowledge of the domain is important
If you don’t have a good understanding of WordPress (or javascript, or servers, or etc.), your ability to analyze the demonstrated symptoms is limited.
A good understanding of the problem is important
- The more information we have the better suited we are to fix the problem.
- If we don’t have enough information, we should gather more.
Collecting the right information
- “I did, I saw, I expected.”
- Reproduction steps, screenshots, environment data
- Common patterns between the symptoms
Collect more than you need, then filter out the garbage.
It’s important to check all your bases and avoid letting assumptions trap you (“oh, it can’t be because of this, because of that…”)
5 Whys
Asking “Why?” is one of your most powerful tools!
The Problem: The YouTube video in my post isn’t showing up.
- Why? It’s not being rendered on the page.
- Why? The shortcode callback isn’t outputting anything.
- Why? The attribute check is failing.
- Why? The ID has a hyphen in it.
- Why? YouTube changed its IDs.
(Not limited to 5!)
Always go to the source!
Start at the point of origin for the symptoms and work backwards until you pinpoint the cause of the bug.
Fatal error: Call to undefined function give_me_some_seos() in /Users/moo/Sites/wp/wp-content/themes/twentyeleven/functions.php on line 76
Point of origin
We need to determine where the problem is occurring and eventually originating:
- server-level
- database-level
- application-level (server or client-side)
- user-level
This will drive our investigation.
Tools
WordPress makes debugging easy.
Many built-in tools. More pluggable ones.
defined( ‘CONSTANTS’ );
define( 'WP_DEBUG', true );
Enables error reporting.
If your development environment does not have this set to true, you’re _doing_it_wrong().
defined( ‘CONSTANTS’ );
define( 'WP_DEBUG_DISPLAY', true ); // display_errors define( 'WP_DEBUG_LOG', '/tmp/php_errors.log' ); // write errors to file

defined( ‘CONSTANTS’ );
Debugging SQL:
define( 'SAVEQUERIES', true );
Debugging CSS and JS:
define( 'SCRIPT_DEBUG', true ); define( 'CONCATENATE_SCRIPTS', false ); define( 'COMPRESS_SCRIPTS', false ); define( 'COMPRESS_CSS', false ); define( 'ENFORCE_GZIP', false );
Debug Bar
Firebug for your WordPress
http://wordpress.org/extend/plugins/debug-bar/
If your development environment does not have this set to installed and activated, you’re _doing_it_wrong().
Debug Bar Extender
Variable trace, performance profiling, and more.
Debug Bar Extender
if ( class_exists( 'Debug_Bar_Extender' ) ) {
add_action( 'wp_head', 'my_debug_trace_id' );
add_action( 'comments_template', 'my_debug_trace_id' );
add_action( 'wp_footer', 'my_debug_trace_id' );
function my_debug_trace_id() {
$post_id = get_the_ID();
Debug_Bar_Extender::instance()->trace_var( $post_id, 'post_id via get_the_ID()' );
}
}
Debug Bar Extender
Profiler example.
Debug Bar: Roll Your Own
<?php
// Tracks remote requests made using the WordPress HTTP API and displays their results
class WPCOM_Debug_Bar_Remote_Requests extends Debug_Bar_Panel {
var $ignore_urls = array(
'http://127.0.0.1/wp-cron.php?doing_wp_cron',
);
var $requests = array();
var $status_counts = array();
var $current_request = array();
function init() {
$this->title( __('Remote Requests', 'debug-bar') );
add_filter( 'http_request_args', array( $this, 'log_http_requests' ), 99, 2 ); // after all args have been set up
add_action( 'http_api_debug', array( $this, 'log_http_request_result' ), 0, 5 ); // as soon as it's complete
$this->status_counts = array(
'Informational (1xx)' => 0,
'Success (2xx)' => 0,
'Multiple Choices (3xx)' => 0,
'Client Error (4xx)' => 0,
'Server Error (5xx)' => 0,
'Unknown' => 0,
);
}
function render() {
global $wp;
?>
<div id='debug-bar-remote-requests'>
<h2>
<span>Total Time</span>
<?php echo number_format( $this->total_time, 2 ) . 's'; ?>
</h2>
// ...
Other Developer Plugins
- Debug bar Transients
- Log Deprecated Notices
- Monster Widget
- Rewrite Rule Inspector
Homegrown
- Hardcoded checkpoints (
var_dump) (don’t discount this; it’s ghetto but powerful) - error_log (tail + xmpp on WP.com)
- debug_backtrace
- alert / console.log (for javascript)
- Roll your own…
var_dump
Index: capabilities.php =================================================================== --- capabilities.php (revision 20851) +++ capabilities.php (working copy) @@ -1314,6 +1314,10 @@ if ( ! isset( $wp_roles ) ) $wp_roles = new WP_Roles(); + // Debug + echo 'Adding role:<br/>'; + var_dump( $role, $display_name, $capabilities ); + return $wp_roles->add_role( $role, $display_name, $capabilities ); }
error_log
Index: capabilities.php =================================================================== --- capabilities.php (revision 20851) +++ capabilities.php (working copy) @@ -1314,6 +1314,10 @@ if ( ! isset( $wp_roles ) ) $wp_roles = new WP_Roles(); + // Debug + error_log( 'Adding role: ' . var_export( array( $role, $display_name, $capabilities ), true ) ); + return $wp_roles->add_role( $role, $display_name, $capabilities ); }
Homegrown: sekret “all” filter
add_action( 'all', function() {
var_dump( current_filter() );
} );
Homegrown: dump all callbacks for a hook
function debug_get_functions_for_hook( $hook ) {
global $wp_filter;
if( ! isset( $wp_filter[$hook] ) )
return;
$functions = array();
foreach( (array) $wp_filter[$hook] as $key => $actions ) {
$functions['priority_' . $key] = array_keys( $actions );
}
ksort( $functions );
return $functions;
}
Mfields::dump_hook( 'the_content' );
Non-WordPress Tools
- Web Inspector / Firebug
- curl / Charles
- Xdebug and integrated debugging for IDEs
Xdebug
Problem: The Fatal Whitescreen

Problem: The Fatal Whitescreen
With define( 'WP_DEBUG', true );:

(in production, look at your error log)
Problem: The Fatal Whitescreen
Is the function defined anywhere?
$ ack "give_me_magic_and_seos"
Problem: Filters gone bad
Content showing for some posts but not others.
Problem: Filters gone bad
Content showing for some posts but not others.
Problem: Filters gone bad
Step 0. Verify the post content!
Problem: Filters gone bad
Step 1. Where and how is the content being output?
Goto our theme, content.php:
<div class="entry-content">
<?php the_content( __( 'Continue reading <span class="meta-nav">→</span>', 'twentyeleven' ) ); ?>
<?php wp_link_pages( array( 'before' => '<div class="page-link"><span>' . __( 'Pages:', 'twentyeleven' ) . '</span>', 'after' => '</div>' ) ); ?>
</div><!-- .entry-content -->
Problem: Filters gone bad
Step 2. What does the_content() do?
Goto core: http://core.trac.wordpress.org/browser/tags/3.3.1/wp-includes/post-template.php#L157
$content = get_the_content($more_link_text, $stripteaser);
$content = apply_filters('the_content', $content);
Problem: Filters gone bad
Step 3. What is manipulating the_content()?
var_dump( debug_get_functions_for_hook( 'the_content' ) );
/*
Here's what we get:
array(4) {
["priority_1"] =>
array(1) { [0]=> string(14) "polldaddy_link" }
["priority_10"] =>
array(7) { [0]=> string(11) "wptexturize" [1]=> string(15) "convert_smilies" [2]=> string(13) "convert_chars" [3]=> string(7) "wpautop" [4]=> string(17) "shortcode_unautop" [5]=> string(18) "prepend_attachment" [6]=> string(25) "my_content_with_thumbnail" }
["priority_11"] =>
array(2) { [0]=> string(16) "capital_P_dangit" [1]=> string(12) "do_shortcode" }
["priority_8"] =>
array(2) { [0]=> string(45) "0000000072f50cfc0000000022e7a2b6run_shortcode" [1]=> string(41) "0000000072f50cfc0000000022e7a2b6autoembed" } }
*/
Problem: Filters gone bad
Step 4. Investigate all callbacks.
add_filter( 'the_content', 'my_content_with_thumbnail' );
function my_content_with_thumbnail( $content ) {
if ( has_post_thumbnail() ) {
return get_the_post_thumbnail() . $content;
}
// THIS IS OUR PROBLEM! A MISSING RETURN!
}
Problem: Comments disappearing!
Comments for a post (in fact, many posts) are “disappearing” when submitted.
Questions to ask:
* Did they actually disappear?
* Are they in the database?
* Are they flagged as spam?
* Are they being fetched correctly?
* Are they being fetched at all?
* Are they attached to the correct post?
Not all problems are obvious or visible
For example: cron event fails… but only sometimes.
How do we investigate these? Get Creative :)
(Remote logging; hacks, hacks, hacks)
Debugging is an art
- Many right ways to do it. Many wrong (but the only) ways to do it.
- Investigate, distill, hypothesize, test, conclude, solve.
- Understand your domain and continually learn.
- Understand and use your tools.
Questions?
- http://digitalize.ca
- batmoo (.org)
- mjangda (Github)
- @mjangda
Interesting plugin, thanks for shaing, I will be checking it out!
Pingback: A Developer’s Highlights from WordCamp NYC 2012 | Oomph
Pingback: Debugging WordPress with XDebug - Jean Galea | Web Designer and Developer in Malta | WordPress Consultant
Pingback: WordCamp Toronto Session Notes and Slides - WP Daily