Completed
Push — add/docker-scripts ( 893b03...7bb1a9 )
by
unknown
10:29
created

debug.php ➔ lo()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 1
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
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 )
0 ignored issues
show
Bug Best Practice introduced by
The expression $ob_status of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

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.

Loading history...
87
				break;
88
			foreach ( $ob_status as $ob )
89
				$obs[] = $ob['name'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$obs was never initialized. Although not strictly required by PHP, it is generally a good practice to add $obs = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
90
			// This is not perfect: anonymous handlers appear as default.
91
			if ( $obs == array( 'default output handler' ) )
0 ignored issues
show
Bug introduced by
The variable $obs does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
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;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$bts was never initialized. Although not strictly required by PHP, it is generally a good practice to add $bts = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
100
			}
101
			if ( array_intersect( $obs, $bts ) )
0 ignored issues
show
Bug introduced by
The variable $bts does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
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