Debugging in WordPress


WordCamp San Francisco 2011
August 13, 2011
Andrew Nacin
Core Developer of WordPress
Tech Ninja at Audrey Capital
nacin@wordpress.org
@nacin on Twitter
var_dump( $data );
      die( );
      // fin.
“I love suppressing error
messages!” — no one, ever.
So let's start with
WP_DEBUG
define( 'WP_DEBUG', true );
What WP_DEBUG does:
— Sets the error reporting level to
include PHP notices
— Bypasses some error suppression
— Triggers notices for deprecated
usage (more on that later)
You can change its output:
// A default of true forces on display_errors.
define( 'WP_DEBUG_DISPLAY', true );

// Setting it to false will let PHP (php.ini) decide.
define( 'WP_DEBUG_DISPLAY', false );
// So, if your php.ini has display_errors on,
   you'll also need to turn it off:
ini_set( 'display_errors', 0 );
Likely being simplified in 3.3:
// A default of true forces on display_errors.
define( 'WP_DEBUG_DISPLAY', true );

// False forces off display_errors.
define( 'WP_DEBUG_DISPLAY', false );

// Null will let PHP (php.ini) decide.
define( 'WP_DEBUG_DISPLAY', null );
You can specify an error log:
// wp-content/debug.log
define( 'WP_DEBUG_LOG', true );

// New in 3.3! (Likely.)
define( 'WP_DEBUG_LOG', '/tmp/php-errors' );

// Follow ticket #18391, created last night.
Debug admin CSS & JS with
SCRIPT_DEBUG
define( 'SCRIPT_DEBUG', true );
SCRIPT_DEBUG prevents
minification and concatenation of
core scripts and styles.

load-scripts.php?c=1&load=global,wp-admin,ms…
versus global.dev.css, wp-admin.dev.css, ms.dev.css
SCRIPT_DEBUG is good for:
— Finding bugs in your admin screens
— Studying the admin theme
— Contributing to core
Debug queries with
SAVEQUERIES
define( 'SAVEQUERIES', true );
SAVEQUERIES stores query data in
$wpdb->queries.

(OMG BBQ:
This is NOT for production.)
Query: SELECT * FROM wp_users
WHERE user_login = 'nacin'
Backtrace: WP->init,
wp_get_current_user,
get_currentuserinfo,
wp_validate_auth_cookie,
get_user_by
Execution time: 0.4ms
On using these in production:
WP_DEBUG
— Don't display errors
— Don't log to wp-content/error.log
SCRIPT_DEBUG
— Bad idea.
SAVEQUERIES
— Barry will hunt you down.
Plugins
Your new best friend:
The Debug Bar
It’s like Firebug for your WordPress.
Debug Bar Console
A Firebug-like console for
PHP and MySQL (really)
What's stopping you from
building your own Debug
Bar extension?
Nothing.
Log Deprecated Notices
— Logs the usage of deprecated files,
functions, and function arguments.
— Pinpoints where the deprecated
functionality is being used.
— NEVER use in production.
Theme Check
— Test your theme for the latest
WordPress standards and practices.
— Required for the theme directory.
Common functions when
debugging
— error_log and var_export / print_r
— var_dump and sometimes die
— debug_backtrace
Tracking it down
Step 1
Disable plugins
Switch to default theme
What might be left behind?
Cron, Rewrites,
Roles/Capabilities
Drop-ins
— advanced-cache.php
— db.php
— object-cache.php
— mu-plugins
What's going on?
Query vars set properly?
Main query SQL look right?
Rewrite rule matched?
Funny redirect?
Confirm it's canonical.
remove_action( 'template_redirect',
                   'redirect_canonical' );
Testing multisite?


I leave this in my config:
// define( 'MULTISITE', true );
Dig into the codebase.
Your friends are phpxref, grep.
Learn the stack.
Learn what things call what.
Some common traps
Step 1
Is your code even running?
(Spell the hook right?)
die( 'wtf' ); or error_log( )
White screen on the
frontend, no errors?

Appearance > Themes
User interface doesn't work?
Check the browser's JS console
for an error, usually a conflict.
Internal server errors?
Infinite redirects. Check:
— home and siteurl
— then .htaccess
— then canonical
Widgets got moved around?
Do the sidebars have IDs?
Otherwise, it's the order in which
register_sidebar() is called.
Lots of pages? Slow site?
My next Q: What's your
permalink structure?
No longer an issue in 3.3!
Weird Bug #1
Somewhere deep in style.css:
“Template: home.php”
Miscalculated as a multisite bug:
activated on single site before
style.css had the headers.
Weird Bug #2
The admin looked weird
Weird Bug #2
The admin looked weird

Trigger: Upgrade from 2.5 to 3.1
Weird Bug #2
The admin looked weird

Trigger: Upgrade from 2.5 to 3.1
Problem: No MySQL ALTER
permissions
Weird Bug #3
Some bbPress rewrites
failed after activation
Weird Bug #3
Some bbPress rewrites
failed after activation
Problem: Custom post type
rewrites aren't registered on
activation
// Say you have:
add_action( 'generate_rewrite_rules',
               function( $wp_rewrite ) { … } );
// And:
register_activation_hook( __FILE__, function() {
     flush_rewrite_rules( );
} );
// But! This won't work for CPTs. Try this:
add_action( 'init', 'my_register_post_types' );
register_activation_hook( __FILE__, function( ) {
     my_register_post_types( );
     flush_rewrite_rules( );
} );
Local Development
/etc/hosts
127.0.0.1 andrewnacin.com
Configure virtual hosts
Install local WordPress
Replace links with an
output buffer for
development or staging
environments
// Run this on development or staging.
// A development wp-config.php
   or local-config.php is fine.

ob_start( 'nacin_dev_urls' );
function nacin_dev_urls( $buffer ) {
  $live = 'http://andrewnacin.com';
  $dev = 'http://dev.andrewnacin.com';
  return str_replace( $live, $dev, $buffer );
}
URLs are more portable
when they're absolute.

Really.*

The serialized stuff is lame though.
Start of a conversation
Xdebug (backtraces, profiling)
KCacheGrind (visualization)
Using an IDE
Unit testing
And finally, remember:
Friends don't let friends
develop without
WP_DEBUG.
Thanks! Questions?


@nacin

WordCamp SF 2011: Debugging in WordPress

  • 1.
    Debugging in WordPress WordCampSan Francisco 2011 August 13, 2011
  • 2.
    Andrew Nacin Core Developerof WordPress Tech Ninja at Audrey Capital nacin@wordpress.org @nacin on Twitter
  • 3.
    var_dump( $data ); die( ); // fin.
  • 4.
    “I love suppressingerror messages!” — no one, ever.
  • 5.
    So let's startwith WP_DEBUG define( 'WP_DEBUG', true );
  • 6.
    What WP_DEBUG does: —Sets the error reporting level to include PHP notices — Bypasses some error suppression — Triggers notices for deprecated usage (more on that later)
  • 7.
    You can changeits output: // A default of true forces on display_errors. define( 'WP_DEBUG_DISPLAY', true ); // Setting it to false will let PHP (php.ini) decide. define( 'WP_DEBUG_DISPLAY', false ); // So, if your php.ini has display_errors on, you'll also need to turn it off: ini_set( 'display_errors', 0 );
  • 8.
    Likely being simplifiedin 3.3: // A default of true forces on display_errors. define( 'WP_DEBUG_DISPLAY', true ); // False forces off display_errors. define( 'WP_DEBUG_DISPLAY', false ); // Null will let PHP (php.ini) decide. define( 'WP_DEBUG_DISPLAY', null );
  • 9.
    You can specifyan error log: // wp-content/debug.log define( 'WP_DEBUG_LOG', true ); // New in 3.3! (Likely.) define( 'WP_DEBUG_LOG', '/tmp/php-errors' ); // Follow ticket #18391, created last night.
  • 10.
    Debug admin CSS& JS with SCRIPT_DEBUG define( 'SCRIPT_DEBUG', true );
  • 11.
    SCRIPT_DEBUG prevents minification andconcatenation of core scripts and styles. load-scripts.php?c=1&load=global,wp-admin,ms… versus global.dev.css, wp-admin.dev.css, ms.dev.css
  • 12.
    SCRIPT_DEBUG is goodfor: — Finding bugs in your admin screens — Studying the admin theme — Contributing to core
  • 13.
  • 14.
    SAVEQUERIES stores querydata in $wpdb->queries. (OMG BBQ: This is NOT for production.)
  • 15.
    Query: SELECT *FROM wp_users WHERE user_login = 'nacin' Backtrace: WP->init, wp_get_current_user, get_currentuserinfo, wp_validate_auth_cookie, get_user_by Execution time: 0.4ms
  • 16.
    On using thesein production: WP_DEBUG — Don't display errors — Don't log to wp-content/error.log SCRIPT_DEBUG — Bad idea. SAVEQUERIES — Barry will hunt you down.
  • 17.
  • 18.
    Your new bestfriend: The Debug Bar It’s like Firebug for your WordPress.
  • 19.
    Debug Bar Console AFirebug-like console for PHP and MySQL (really)
  • 20.
    What's stopping youfrom building your own Debug Bar extension? Nothing.
  • 21.
    Log Deprecated Notices —Logs the usage of deprecated files, functions, and function arguments. — Pinpoints where the deprecated functionality is being used. — NEVER use in production.
  • 22.
    Theme Check — Testyour theme for the latest WordPress standards and practices. — Required for the theme directory.
  • 23.
    Common functions when debugging —error_log and var_export / print_r — var_dump and sometimes die — debug_backtrace
  • 24.
  • 25.
  • 26.
    What might beleft behind? Cron, Rewrites, Roles/Capabilities
  • 27.
    Drop-ins — advanced-cache.php — db.php —object-cache.php — mu-plugins
  • 28.
    What's going on? Queryvars set properly? Main query SQL look right? Rewrite rule matched?
  • 29.
    Funny redirect? Confirm it'scanonical. remove_action( 'template_redirect', 'redirect_canonical' );
  • 30.
    Testing multisite? I leavethis in my config: // define( 'MULTISITE', true );
  • 31.
    Dig into thecodebase. Your friends are phpxref, grep. Learn the stack. Learn what things call what.
  • 32.
  • 33.
    Step 1 Is yourcode even running? (Spell the hook right?) die( 'wtf' ); or error_log( )
  • 34.
    White screen onthe frontend, no errors? Appearance > Themes
  • 35.
    User interface doesn'twork? Check the browser's JS console for an error, usually a conflict.
  • 36.
    Internal server errors? Infiniteredirects. Check: — home and siteurl — then .htaccess — then canonical
  • 37.
    Widgets got movedaround? Do the sidebars have IDs? Otherwise, it's the order in which register_sidebar() is called.
  • 38.
    Lots of pages?Slow site? My next Q: What's your permalink structure? No longer an issue in 3.3!
  • 39.
    Weird Bug #1 Somewheredeep in style.css: “Template: home.php” Miscalculated as a multisite bug: activated on single site before style.css had the headers.
  • 40.
    Weird Bug #2 Theadmin looked weird
  • 41.
    Weird Bug #2 Theadmin looked weird Trigger: Upgrade from 2.5 to 3.1
  • 42.
    Weird Bug #2 Theadmin looked weird Trigger: Upgrade from 2.5 to 3.1 Problem: No MySQL ALTER permissions
  • 43.
    Weird Bug #3 SomebbPress rewrites failed after activation
  • 44.
    Weird Bug #3 SomebbPress rewrites failed after activation Problem: Custom post type rewrites aren't registered on activation
  • 45.
    // Say youhave: add_action( 'generate_rewrite_rules', function( $wp_rewrite ) { … } ); // And: register_activation_hook( __FILE__, function() { flush_rewrite_rules( ); } ); // But! This won't work for CPTs. Try this: add_action( 'init', 'my_register_post_types' ); register_activation_hook( __FILE__, function( ) { my_register_post_types( ); flush_rewrite_rules( ); } );
  • 46.
  • 47.
  • 48.
    Replace links withan output buffer for development or staging environments
  • 49.
    // Run thison development or staging. // A development wp-config.php or local-config.php is fine. ob_start( 'nacin_dev_urls' ); function nacin_dev_urls( $buffer ) { $live = 'http://andrewnacin.com'; $dev = 'http://dev.andrewnacin.com'; return str_replace( $live, $dev, $buffer ); }
  • 50.
    URLs are moreportable when they're absolute. Really.* The serialized stuff is lame though.
  • 51.
    Start of aconversation Xdebug (backtraces, profiling) KCacheGrind (visualization) Using an IDE Unit testing
  • 52.
    And finally, remember: Friendsdon't let friends develop without WP_DEBUG.
  • 53.