WordPress Debugging (WordCamp NYC 2012 Talk)

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!)

http://en.wikipedia.org/wiki/5_Whys

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.

http://wordpress.org/extend/plugins/debug-bar-extender/

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

Let’s work through some examples!

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">&rarr;</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.



Posted from New York, New York, United States.

This entry was posted in Blog, WordPress. Bookmark the permalink.

4 Responses to WordPress Debugging (WordCamp NYC 2012 Talk)

  1. Tom says:

    Interesting plugin, thanks for shaing, I will be checking it out!

  2. Pingback: A Developer’s Highlights from WordCamp NYC 2012 | Oomph

  3. Pingback: Debugging WordPress with XDebug - Jean Galea | Web Designer and Developer in Malta | WordPress Consultant

  4. Pingback: WordCamp Toronto Session Notes and Slides - WP Daily

Leave a Reply