morganestes
5/31/2013 - 1:59 PM

Debugging WordPress with the JavaScript Console

Debugging WordPress with the JavaScript Console

<?php
/**
 * WordPress Console Debug
 *
 * Debug PHP using the JavaScript console.
 * Enable this plugin and view your console for more information on how to use it.
 *
 * @package   WP_console_debug
 * @author    DRSK
 * @license   WTFPL
 *
 * @wordpress-plugin
 * Plugin Name: WordPress Console Debug
 * Description: Debug PHP using the JavaScript console. View your console after activation for more details.
 * Version:     1.0
 * Author:      DRSK
 */
 
// If this file is called directly
if( !defined( 'WP_PLUGIN_URL' ) || !defined( 'WPINC' ) )
	die( 'Restricted access' );

// WP_console_debug
class WP_console_debug {
	/**
	 * Static variables
	 */
		static $console_debug = null;
		
	/**
	 * Initialization of the plugin
	 */
		function __construct() {
			// set up constants for debugging types
			if( !defined( 'LOG' ) )    	define( 'LOG', 1 );
			if( !defined( 'INFO' ) )   	define( 'INFO', 2 );
			if( !defined( 'WARN' ) )   	define( 'WARN', 3 );
			if( !defined( 'ERROR' ) )  	define( 'ERROR', 4 );
			
			// new line
			define( 'NL', "\r\n" );

			// add debugging to footers
			add_action( 'wp_footer', array( $this, 'print_console' ) );
			add_action( 'admin_footer', array( $this, 'print_console' ) );
		}
		 
	/** 
	 * Create debugging output in JavaScript
	 */
		public function debug( $name = null, $var = null, $type = LOG ) {
			global $wpdb, $template;
				
			// debugging template files
			if( $name == TEMPLATE )
				$name = basename( $template );
		
			// fallback: if an object or array has been called as $name
			if( is_object( $name ) || is_array( $name ) ) {
				$var = $name;
				$name = null;
			}

			// gather information about the call
			$debug_call = $this->track_debug_call();

			// start output
			if( $debug_call && ( $name || $var ) )
				$output = 'console.debug("Debug from '. str_replace( array( "\\" ), "\\\\", $debug_call['file'] ) . ' @ line ' . $debug_call['line']. ':");'.NL;
			elseif( $debug_call && ( !$name || !$var ) )
				$output = 'console.error("Something went wrong. Please check ' . str_replace( array( "\\" ), "\\\\", $debug_call['file'] ) . ' @ line ' . $debug_call['line']. '.");'.NL;
			
			// standard output
			if( $name && ( is_string( $name ) || is_int( $name ) ) ) {
				switch( $type ) {
					case LOG:
						$output .= 'console.log("'.$name.'");'.NL;    
					break;
					case INFO:
						$output .= 'console.info("'.$name.'");'.NL;    
					break;
					case WARN:
						$output .= 'console.warn("'.$name.'");'.NL;    
					break;
					case ERROR:
						$output .= 'console.error("'.$name.'");'.NL;    
					break;
				}
			}

			// if a variable has been set (or $name is an object or array)
			if( !empty( $var ) ) {
				if( is_object( $var ) || is_array( $var ) ) {
					// JSON encode the $var and let the console do its magic
					if( $var )
						$object = json_encode( $var );
					else
						$object = 'No data available';
					
					// uniquify each variable, just in case
					if( !$name )
						$name = intval( $debug_call['line'] );
						
					// create unique JavaScript variables based on $name
					$output .= 'var object'.preg_replace( '~[^A-Z|0-9]~i', "_", $name ).' = \''.str_replace( array( "'", '\"', '\r\n', '\n' ), "\'", $object ).'\';'.NL;
					$output .= 'var val'.preg_replace( '~[^A-Z|0-9]~i', "_", $name ).' = eval("(" + object'.preg_replace( '~[^A-Z|0-9]~i', "_", $name ).' + ")" );'.NL;
					
					// output for objects and arrays
					switch($type) {
						case LOG:
							$output .= 'console.debug(val'.preg_replace( '~[^A-Z|0-9]~i', "_", $name ).');'.NL;    
						break;
						case INFO:
							$output .= 'console.info(val'.preg_replace( '~[^A-Z|0-9]~i', "_", $name ).');'.NL;
						break;
						case WARN:
							$output .= 'console.warn(val'.preg_replace( '~[^A-Z|0-9]~i', "_", $name ).');'.NL;        
						break;
						case ERROR:
							$output .= 'console.error(val'.preg_replace( '~[^A-Z|0-9]~i', "_", $name ).');'.NL;    
						break;
					}
				} else {
					// output for variables
					switch($type) {
						case LOG:
							$output .= 'console.debug("'.str_replace( '"', '\\"', $var ).'");'.NL;
						break;
						case INFO:
							$output .= 'console.info("'.str_replace( '"', '\\"', $var ).'");'.NL;
						break;
						case WARN:
							$output .= 'console.warn("'.str_replace( '"', '\\"', $var ).'");'.NL;    
						break;
						case ERROR:
							$output .= 'console.error("'.str_replace( '"', '\\"', $var ).'");'.NL;
						break;
					}
				}
			}
			
			// assign the output to the static variable as an array
			self::$console_debug[] = $output;
		}

	/**
	 * Trackback call data from function debug
	 */
		private function track_debug_call() {
			$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
			// locate the debugging
			if( 'console_debug' == $backtrace[2]['function'] )
				return $backtrace[2];
			else
				return false;
		}
	
	/**
	 * Output the debugging and help menu
	 */
		public function print_console() {
			// start WP Console Debug
			echo NL.'<!-- WordPress Console Debug -->';
			
			// IE and other browsers without a good console
			$ie = NL.'<!-- Internet Explorer -->' . NL .'<script type="text/javascript">'.NL;
			$ie .= '(function(f){f||(f=window.console={log:function(a,b,c,d,e){},info:function(a,b,c,d,e){},warn:function(a,b,c,d,e){},error:function(a,b,c,d,e){}});if(!Function.prototype.bind){Function.prototype.bind=function(a){var b=this,args=Array.prototype.slice.call(arguments,1);return function(){return b.apply(a,Array.prototype.concat.apply(args,arguments))}}}if(typeof(f.log)===\'object\'){f.log=Function.prototype.call.bind(f.log,f);f.info=Function.prototype.call.bind(f.info,f);f.warn=Function.prototype.call.bind(f.warn,f);f.error=Function.prototype.call.bind(f.error,f)}(\'group\'in f)||(f.group=function(a){f.info("\n------------\n"+a+"\n------------")});(\'groupEnd\'in f)||(f.groupEnd=function(){});(\'time\'in f)||(function(){var c={};f.time=function(a){c[a]=new Date().getTime()};f.timeEnd=function(a){var b=new Date().getTime(),time=(a in c)?b-c[a]:0;f.info(a+\': \'+time+\'ms\')}}())})(window.console);';
			$ie .= 'if (!window.console) console = {};';
			$ie .= 'console.log = console.log || function(){};';
			$ie .= 'console.warn = console.warn || function(){};';
			$ie .= 'console.error = console.error || function(){};';
			$ie .= 'console.info = console.info || function(){};';
			$ie .= 'console.debug = console.debug || function(){};';
			$ie .= 'console.groupCollapsed = console.groupCollapsed || function(){};';
			$ie .= 'console.groupEnd = console.groupEnd || function(){};';
			$ie .= '</script>';
			echo $ie;
			
			// set up help menu 
			$help = NL.'<!-- Help Menu -->' . NL .'<script type="text/javascript">';
			$help .= 'console.groupCollapsed("WordPress Console Debug");'.NL;
			$help .= 'console.log("To hide this menu, use: define( \'WPDC_HELP\', false );");'.NL;
			$help .= 'console.warn("Note: the console is very limited in IE and does not work as intended.");'.NL;
			$help .= 'console.groupCollapsed("Standard debugging");'.NL;
			$help .= 'console.debug("Examples for simple debugging");'.NL;
			$help .= 'console.log("// Simple message to console \nconsole_debug(\"A very simple message\"); \n \n// Variable to console \n$x = 3; \n$y = 5; \n$z = $x/$y; \nconsole_debug(\"Variable Z: \", $z); \n \n// A warning \nconsole_debug(\"A simple Warning\", null, WARN); \n \n// Info \nconsole_debug(\"A simple Info message\", null, INFO); \n \n// An error \nconsole_debug(\"A simple error messsage\", null, ERROR); \n \n// Array in console \n$fruits = array(\"banana\", \"apple\", \"strawberry\", \"pineaple\"); \n$fruits = array_reverse($fruits); \nconsole_debug(\"Fruits array\", $fruits); \n \n \n// Object to console \n$book               = new stdClass; \n$book->title        = \"Harry Potter and the Prisoner of Azkaban\"; \n$book->author       = \"J. K. Rowling\"; \n$book->publisher    = \"Arthur A. Levine Books\"; \n$book->amazon_link  = \"http://www.amazon.com/dp/0439136369/\"; \nconsole_debug(\"Object\", $book);");'.NL;
			$help .= 'console.groupEnd();'.NL;
			$help .= 'console.groupCollapsed("WordPress Debugging");'.NL;
			$help .= 'console.debug("Examples for WordPress debugging");'.NL;
			$help .= 'console.log("console_debug(get_the_id());\nconsole_debug(\"Post name:\", $post->post_name);\nconsole_debug(\"Post meta:\", get_post_meta( $post->ID ));\n\n//Check template\nWithin a template: console_debug( TEMPLATE );\nCheck all templates: add_action( \'the_content\', function( $content ) { return $content . console_debug( TEMPLATE ); } );\n\n//Check post data\nWithin a teplate: console_debug( $post );\nCheck post data everywhere: add_action( \'the_content\', function( $content ) { global $post; return $content . console_debug( $post ); } );");'.NL;
			$help .= 'console.groupEnd();'.NL;
			$help .= 'console.groupEnd();'.NL;
			$help .= '</script>';
			
			// if help constant is false, don't show the menu
			if( ( defined( 'WPDC_HELP' ) && WPDC_HELP !== FALSE ) || !defined( 'WPDC_HELP' ) )
				echo $help;
			
			// loop debugs
			if( self::$console_debug ) {
				$debug_output = NL.'<!-- Debugging -->' . NL .'<script type="text/javascript">'.NL;
				foreach( self::$console_debug as $debug )
					$debug_output .= $debug;
				$debug_output .= '</script>';
				echo $debug_output;
			}
			
			echo NL.'<!-- end: WordPress console debug -->'.NL;
		}
}


// create new instance of the class
$wp_console_debug = new WP_console_debug();

// debug function
function console_debug( $name = null, $var = null, $type = LOG  ) {
	global $wp_console_debug;
	return $wp_console_debug->debug( $name, $var, $type );
}