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