1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
Plugin Name: Automattic Debug Helpers |
5
|
|
|
Description: <code>l( 'Code is Poetry' )</code> |
6
|
|
|
Version: 1.0 |
7
|
|
|
Author: Automattic |
8
|
|
|
Author URI: http://automattic.com/ |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
|
12
|
|
|
/** |
13
|
|
|
* debug.php |
14
|
|
|
* |
15
|
|
|
* This file contains helpful debugging functions |
16
|
|
|
*/ |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* l() -- sweet error logging |
20
|
|
|
* |
21
|
|
|
* l($something_to_log); // error_log(print_r($something_to_log, true)); |
22
|
|
|
* l(compact('v1','v2'); // log several variables with labels |
23
|
|
|
* l($thing5, $thing10); // log two things |
24
|
|
|
* l(); // log the file:line |
25
|
|
|
* l(null, $stuff, $ba); // log the file:line, then log two things. |
26
|
|
|
* |
27
|
|
|
* The first call of l() will print an extra line containing a random ID & PID |
28
|
|
|
* and the script name or URL. The ID prefixes every l() log entry thereafter. |
29
|
|
|
* The extra line and ID will help you to indentify and correlate log entries. |
30
|
|
|
* |
31
|
|
|
* Example: |
32
|
|
|
* wpsh> l('yo') |
33
|
|
|
* wpsh> l('dude') |
34
|
|
|
* /tmp/php-errors: |
35
|
|
|
* [21-Jun-2012 14:45:13] 1566-32201 => /home/wpcom/public_html/bin/wpshell/wpshell.php |
36
|
|
|
* [21-Jun-2012 14:45:13] 1566-32201 yo |
37
|
|
|
* [21-Jun-2012 14:50:23] 1566-32201 dude |
38
|
|
|
* |
39
|
|
|
* l() returns its input so you can safely wrap most kinds of expressions to log them. |
40
|
|
|
* l($arg1, $arg2) will call l($arg1) and l($arg2) and then return $arg1. |
41
|
|
|
* |
42
|
|
|
* A null argument will log the file and line number of the l() call. |
43
|
|
|
*/ |
44
|
|
|
function l( $stuff = null ) { |
45
|
|
|
// Do nothing when debugging is off |
46
|
|
|
if ( !defined( 'WP_DEBUG' ) || WP_DEBUG === false ) { |
47
|
|
|
return $stuff; |
48
|
|
|
} |
49
|
|
|
static $pageload; |
50
|
|
|
// Call l() on each argument |
51
|
|
|
if ( func_num_args() > 1 ) { |
52
|
|
|
foreach ( func_get_args() as $arg ) |
53
|
|
|
l($arg); |
54
|
|
|
return $stuff; |
55
|
|
|
} |
56
|
|
|
if ( !isset( $pageload ) ) { |
57
|
|
|
$pageload = substr( md5( mt_rand() ), 0, 4 ); |
58
|
|
|
if ( !empty( $_SERVER['argv'] ) ) |
59
|
|
|
$hint = implode( ' ', $_SERVER['argv'] ); |
60
|
|
|
elseif ( isset( $_SERVER['HTTP_HOST'] ) && isset( $_SERVER['REQUEST_URI'] ) ) |
61
|
|
|
$hint = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; |
62
|
|
|
else |
63
|
|
|
$hint = php_sapi_name(); |
64
|
|
|
error_log( sprintf( "[%s-%s => %s]", $pageload, getmypid(), $hint ) ); |
65
|
|
|
} |
66
|
|
|
$pid = $pageload . '-' . getmypid(); |
67
|
|
|
if ( is_null( $stuff ) ) { |
68
|
|
|
// Log the file and line number |
69
|
|
|
$backtrace = debug_backtrace(false); |
70
|
|
View Code Duplication |
while ( isset( $backtrace[1]['function'] ) && $backtrace[1]['function'] == __FUNCTION__ ) |
71
|
|
|
array_shift( $backtrace ); |
72
|
|
|
$log = sprintf( '%s line %d', $backtrace[0]['file'], $backtrace[0]['line'] ); |
73
|
|
|
} elseif ( is_bool( $stuff ) ) { |
74
|
|
|
$log = $stuff ? 'TRUE' : 'FALSE'; |
75
|
|
|
} elseif ( is_scalar( $stuff ) ) { |
76
|
|
|
// Strings and numbers can be logged exactly |
77
|
|
|
$log = $stuff; |
78
|
|
|
} else { |
79
|
|
|
// Are we in an output buffer handler? |
80
|
|
|
// If so, print_r($stuff, true) is fatal so we must avoid that. |
81
|
|
|
// This is not as slow as it looks: <1ms when !$in_ob_handler. |
82
|
|
|
// Using json_encode_pretty() all the time is much slower. |
83
|
|
|
do { |
84
|
|
|
$in_ob_handler = false; |
85
|
|
|
$ob_status = ob_get_status(true); |
86
|
|
|
if ( ! $ob_status ) |
|
|
|
|
87
|
|
|
break; |
88
|
|
|
foreach ( $ob_status as $ob ) |
89
|
|
|
$obs[] = $ob['name']; |
|
|
|
|
90
|
|
|
// This is not perfect: anonymous handlers appear as default. |
91
|
|
|
if ( $obs == array( 'default output handler' ) ) |
|
|
|
|
92
|
|
|
break; |
93
|
|
|
$backtrace = debug_backtrace(false); |
94
|
|
|
foreach ( $backtrace as $level ) { |
95
|
|
|
$caller = ''; |
96
|
|
|
if ( isset( $level['class'] ) ) |
97
|
|
|
$caller = $level['class'] . '::'; |
98
|
|
|
$caller .= $level['function']; |
99
|
|
|
$bts[] = $caller; |
|
|
|
|
100
|
|
|
} |
101
|
|
|
if ( array_intersect( $obs, $bts ) ) |
|
|
|
|
102
|
|
|
$in_ob_handler = true; |
103
|
|
|
} while ( false ); |
104
|
|
|
if ( $in_ob_handler ) |
105
|
|
|
$log = l_json_encode_pretty( $stuff ); |
106
|
|
|
else |
107
|
|
|
$log = print_r( $stuff, true ); |
108
|
|
|
} |
109
|
|
|
error_log( sprintf( "[%s] %s", $pid, $log ) ); |
110
|
|
|
return $stuff; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
// Log only once (suppresses logging on subsequent calls from the same file+line) |
114
|
|
|
function lo( $stuff ) { |
115
|
|
|
static $callers = array(); |
116
|
|
|
$backtrace = debug_backtrace(false); |
117
|
|
|
$caller = md5( $backtrace[0]['file'] . $backtrace[0]['line'] ); |
118
|
|
|
if ( isset( $callers[$caller] ) ) |
119
|
|
|
return $stuff; |
120
|
|
|
$callers[$caller] = true; |
121
|
|
|
$args = func_get_args(); |
122
|
|
|
return call_user_func_array( 'l', $args ); |
123
|
|
|
} |
124
|
|
|
|
125
|
|
|
// Pretty print for JSON (stolen from public.api) |
126
|
|
|
function l_json_encode_pretty( $data ) { |
127
|
|
|
if ( defined( 'JSON_PRETTY_PRINT' ) ) { |
128
|
|
|
// Added in PHP 5.4.0 |
129
|
|
|
return json_encode( $data, JSON_PRETTY_PRINT ); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
// Adapted from http://us3.php.net/manual/en/function.json-encode.php#80339 |
133
|
|
|
$json = json_encode( $data ); |
134
|
|
|
$len = strlen( $json ); |
135
|
|
|
|
136
|
|
|
$tab = "\t"; |
137
|
|
|
$new_json = ""; |
138
|
|
|
$indent_level = 0; |
139
|
|
|
$in_string = false; |
140
|
|
|
|
141
|
|
|
$slashed = false; |
142
|
|
|
for ( $c = 0; $c < $len; $c++ ) { |
143
|
|
|
$char = $json[$c]; |
144
|
|
|
if ( $in_string ) { |
145
|
|
|
if ( '"' === $char && $c > 0 && !$slashed ) { |
146
|
|
|
$in_string = false; |
147
|
|
|
} |
148
|
|
|
$new_json .= $char; |
149
|
|
|
} else { |
150
|
|
|
switch( $char ) { |
151
|
|
|
case '{': |
152
|
|
|
case '[': |
153
|
|
|
$new_json .= $char . "\n" . str_repeat( $tab, ++$indent_level ); |
154
|
|
|
break; |
155
|
|
|
case '}': |
156
|
|
|
case ']': |
157
|
|
|
$new_json .= "\n" . str_repeat( $tab, --$indent_level ) . $char; |
158
|
|
|
break; |
159
|
|
|
case ',': |
160
|
|
|
$new_json .= ",\n" . str_repeat( $tab, $indent_level ); |
161
|
|
|
break; |
162
|
|
|
case ':': |
163
|
|
|
$new_json .= ": "; |
164
|
|
|
break; |
165
|
|
|
case '"': |
166
|
|
|
if ( $c > 0 && !$slashed ) { |
167
|
|
|
$in_string = true; |
168
|
|
|
} |
169
|
|
|
// no break |
170
|
|
|
default: |
171
|
|
|
$new_json .= $char; |
172
|
|
|
break; |
173
|
|
|
} |
174
|
|
|
} |
175
|
|
|
if ( '\\' == $char ) { |
176
|
|
|
$slashed = !$slashed; |
177
|
|
|
} else { |
178
|
|
|
$slashed = false; |
179
|
|
|
} |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
return $new_json; |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
/** |
186
|
|
|
* A timer. Call once to start, call again to stop. Returns a float. |
187
|
|
|
* Calling e($name) with different names permits simultaneous timers. |
188
|
|
|
* |
189
|
|
|
* e('stuff'); |
190
|
|
|
* do_stuff(); |
191
|
|
|
* $elapsed = e('stuff'); |
192
|
|
|
*/ |
193
|
|
|
function e($name = '') { |
194
|
|
|
static $times = array(); |
195
|
|
|
if ( !array_key_exists($name, $times ) ) { |
196
|
|
|
$times[$name] = microtime( true ); |
197
|
|
|
return; |
198
|
|
|
} |
199
|
|
|
$elapsed = microtime( true ) - $times[$name]; |
200
|
|
|
unset( $times[$name] ); |
201
|
|
|
return $elapsed; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* A wrapper for e() which also logs the result with l(). |
206
|
|
|
* Each log entry begins with a tag common to that pageload. |
207
|
|
|
* You can save a keystroke by calling e() then el(). |
208
|
|
|
* |
209
|
|
|
* e($name); |
210
|
|
|
* do_stuff(); |
211
|
|
|
* el($name); |
212
|
|
|
*/ |
213
|
|
|
function el($name = '') { |
214
|
|
|
$elapsed = e( $name ); |
215
|
|
|
if ( $elapsed !== null ) |
216
|
|
|
l( sprintf( "%9.6f e('%s')", $elapsed, $name ) ); |
217
|
|
|
return $elapsed; |
218
|
|
|
} |
219
|
|
|
|
220
|
|
|
/** |
221
|
|
|
* A persistent timer. After the initial call, each call to t() |
222
|
|
|
* will log the file:line and time elapsed since the initial call. |
223
|
|
|
*/ |
224
|
|
|
function t() { |
225
|
|
|
static $start; |
226
|
|
|
$now = microtime( true ); |
227
|
|
|
if ( !isset( $start ) ) |
228
|
|
|
$start = $now; |
229
|
|
|
|
230
|
|
|
$backtrace = debug_backtrace(false); |
231
|
|
View Code Duplication |
while ( isset( $backtrace[1]['function'] ) && $backtrace[1]['function'] == __FUNCTION__ ) |
232
|
|
|
array_shift( $backtrace ); |
233
|
|
|
|
234
|
|
|
$file = $backtrace[0]['file']; |
235
|
|
|
$line = $backtrace[0]['line']; |
236
|
|
|
$format = 't() => %9.6f at %s line %d'; |
237
|
|
|
$elapsed = $now - $start; |
238
|
|
|
l( sprintf( $format, $elapsed, $file, $line ) ); |
239
|
|
|
} |
240
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.