1
|
|
|
<?php |
|
|
|
|
2
|
|
|
/** |
3
|
|
|
* Kint is a zero-setup debugging tool to output information about variables and stack traces prettily and comfortably. |
4
|
|
|
* |
5
|
|
|
* https://github.com/raveren/kint |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
|
9
|
|
|
if ( defined( 'KINT_DIR' ) ) return; |
10
|
|
|
|
11
|
|
|
|
12
|
|
|
define( 'KINT_DIR', dirname( __FILE__ ) . '/' ); |
13
|
|
|
define( 'KINT_PHP53', version_compare( PHP_VERSION, '5.3.0' ) >= 0 ); |
14
|
|
|
|
15
|
|
|
require KINT_DIR . 'config.default.php'; |
16
|
|
|
require KINT_DIR . 'inc/kintVariableData.class.php'; |
17
|
|
|
require KINT_DIR . 'inc/kintParser.class.php'; |
18
|
|
|
require KINT_DIR . 'inc/kintObject.class.php'; |
19
|
|
|
require KINT_DIR . 'decorators/rich.php'; |
20
|
|
|
require KINT_DIR . 'decorators/plain.php'; |
21
|
|
|
|
22
|
|
|
if ( is_readable( KINT_DIR . 'config.php' ) ) { |
23
|
|
|
require KINT_DIR . 'config.php'; |
24
|
|
|
} |
25
|
|
|
|
26
|
|
|
# init settings |
27
|
|
|
if ( !empty( $GLOBALS['_kint_settings'] ) ) { |
28
|
|
|
Kint::enabled( $GLOBALS['_kint_settings']['enabled'] ); |
29
|
|
|
|
30
|
|
|
foreach ( $GLOBALS['_kint_settings'] as $key => $val ) { |
31
|
|
|
property_exists( 'Kint', $key ) and Kint::$$key = $val; |
|
|
|
|
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
unset( $GLOBALS['_kint_settings'], $key, $val ); |
35
|
|
|
} |
36
|
|
|
|
37
|
|
|
class Kint |
38
|
|
|
{ |
39
|
|
|
// these are all public and 1:1 config array keys so you can switch them easily |
40
|
|
|
private static $_enabledMode; # stores mode and active statuses |
41
|
|
|
|
42
|
|
|
public static $returnOutput; |
43
|
|
|
public static $fileLinkFormat; |
44
|
|
|
public static $displayCalledFrom; |
45
|
|
|
public static $charEncodings; |
46
|
|
|
public static $maxStrLength; |
47
|
|
|
public static $appRootDirs; |
48
|
|
|
public static $maxLevels; |
49
|
|
|
public static $theme; |
50
|
|
|
public static $expandedByDefault; |
51
|
|
|
|
52
|
|
|
public static $cliDetection; |
53
|
|
|
public static $cliColors; |
54
|
|
|
|
55
|
|
|
const MODE_RICH = 'r'; |
56
|
|
|
const MODE_WHITESPACE = 'w'; |
57
|
|
|
const MODE_CLI = 'c'; |
58
|
|
|
const MODE_PLAIN = 'p'; |
59
|
|
|
|
60
|
|
|
|
61
|
|
|
public static $aliases = array( |
62
|
|
|
'methods' => array( |
63
|
|
|
array( 'kint', 'dump' ), |
64
|
|
|
array( 'kint', 'trace' ), |
65
|
|
|
), |
66
|
|
|
'functions' => array( |
67
|
|
|
'd', |
68
|
|
|
'dd', |
69
|
|
|
'ddd', |
70
|
|
|
's', |
71
|
|
|
'sd', |
72
|
|
|
) |
73
|
|
|
); |
74
|
|
|
|
75
|
|
|
private static $_firstRun = true; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* Enables or disables Kint, can globally enforce the rendering mode. If called without parameters, returns the |
79
|
|
|
* current mode. |
80
|
|
|
* |
81
|
|
|
* @param mixed $forceMode |
82
|
|
|
* null or void - return current mode |
83
|
|
|
* false - disable (no output) |
84
|
|
|
* true - enable and detect cli automatically |
85
|
|
|
* Kint::MODE_* - enable and force selected mode disregarding detection and function |
86
|
|
|
* shorthand (s()/d()), note that you can still override this |
87
|
|
|
* with the "~" modifier |
88
|
|
|
* |
89
|
|
|
* @return mixed previously set value if a new one is passed |
90
|
|
|
*/ |
91
|
|
|
public static function enabled( $forceMode = null ) |
92
|
|
|
{ |
93
|
|
|
# act both as a setter... |
94
|
|
|
if ( isset( $forceMode ) ) { |
95
|
|
|
$before = self::$_enabledMode; |
96
|
|
|
self::$_enabledMode = $forceMode; |
97
|
|
|
|
98
|
|
|
return $before; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
# ...and a getter |
102
|
|
|
return self::$_enabledMode; |
103
|
|
|
} |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* Prints a debug backtrace, same as Kint::dump(1) |
107
|
|
|
* |
108
|
|
|
* @param array $trace [OPTIONAL] you can pass your own trace, otherwise, `debug_backtrace` will be called |
109
|
|
|
* |
110
|
|
|
* @return mixed |
111
|
|
|
*/ |
112
|
|
|
public static function trace( $trace = null ) |
113
|
|
|
{ |
114
|
|
|
if ( !self::enabled() ) return ''; |
115
|
|
|
|
116
|
|
|
return self::dump( isset( $trace ) ? $trace : debug_backtrace( true ) ); |
117
|
|
|
} |
118
|
|
|
|
119
|
|
|
|
120
|
|
|
/** |
121
|
|
|
* Dump information about variables, accepts any number of parameters, supports modifiers: |
122
|
|
|
* |
123
|
|
|
* clean up any output before kint and place the dump at the top of page: |
124
|
|
|
* - Kint::dump() |
125
|
|
|
* ***** |
126
|
|
|
* expand all nodes on display: |
127
|
|
|
* ! Kint::dump() |
128
|
|
|
* ***** |
129
|
|
|
* dump variables disregarding their depth: |
130
|
|
|
* + Kint::dump() |
131
|
|
|
* ***** |
132
|
|
|
* return output instead of displaying it: |
133
|
|
|
* @ Kint::dump() |
134
|
|
|
* ***** |
135
|
|
|
* force output as plain text |
136
|
|
|
* ~ Kint::dump() |
137
|
|
|
* |
138
|
|
|
* Modifiers are supported by all dump wrapper functions, including Kint::trace(). Space is optional. |
139
|
|
|
* |
140
|
|
|
* |
141
|
|
|
* You can also use the following shorthand to display debug_backtrace(): |
142
|
|
|
* Kint::dump( 1 ); |
143
|
|
|
* |
144
|
|
|
* Passing the result from debug_backtrace() to kint::dump() as a single parameter will display it as trace too: |
145
|
|
|
* $trace = debug_backtrace( true ); |
146
|
|
|
* Kint::dump( $trace ); |
147
|
|
|
* Or simply: |
148
|
|
|
* Kint::dump( debug_backtrace() ); |
149
|
|
|
* |
150
|
|
|
* |
151
|
|
|
* @param mixed $data |
152
|
|
|
* |
153
|
|
|
* @return void|string |
154
|
|
|
*/ |
155
|
|
|
public static function dump( $data = null ) |
156
|
|
|
{ |
157
|
|
|
if ( !self::enabled() ) return ''; |
158
|
|
|
|
159
|
|
|
list( $names, $modifiers, $callee, $previousCaller, $miniTrace ) = self::_getCalleeInfo( |
160
|
|
|
defined( 'DEBUG_BACKTRACE_IGNORE_ARGS' ) |
161
|
|
|
? debug_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ) |
162
|
|
|
: debug_backtrace() |
163
|
|
|
); |
164
|
|
|
$modeOldValue = self::enabled(); |
165
|
|
|
$firstRunOldValue = self::$_firstRun; |
166
|
|
|
|
167
|
|
|
# process modifiers: @, +, !, ~ and - |
168
|
|
|
if ( strpos( $modifiers, '-' ) !== false ) { |
169
|
|
|
self::$_firstRun = true; |
170
|
|
|
while ( ob_get_level() ) { |
171
|
|
|
ob_end_clean(); |
172
|
|
|
} |
173
|
|
|
} |
174
|
|
|
if ( strpos( $modifiers, '!' ) !== false ) { |
175
|
|
|
$expandedByDefaultOldValue = self::$expandedByDefault; |
176
|
|
|
self::$expandedByDefault = true; |
177
|
|
|
} |
178
|
|
|
if ( strpos( $modifiers, '+' ) !== false ) { |
179
|
|
|
$maxLevelsOldValue = self::$maxLevels; |
180
|
|
|
self::$maxLevels = false; |
181
|
|
|
} |
182
|
|
|
if ( strpos( $modifiers, '@' ) !== false ) { |
183
|
|
|
$returnOldValue = self::$returnOutput; |
184
|
|
|
self::$returnOutput = true; |
185
|
|
|
self::$_firstRun = true; |
186
|
|
|
} |
187
|
|
|
if ( strpos( $modifiers, '~' ) !== false ) { |
188
|
|
|
self::enabled( self::MODE_WHITESPACE ); |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
# set mode for current run |
192
|
|
|
$mode = self::enabled(); |
193
|
|
|
if ( $mode === true ) { |
194
|
|
|
$mode = PHP_SAPI === 'cli' |
195
|
|
|
? self::MODE_CLI |
196
|
|
|
: self::MODE_RICH; |
197
|
|
|
} |
198
|
|
|
self::enabled( $mode ); |
199
|
|
|
|
200
|
|
|
$decorator = self::enabled() === self::MODE_RICH |
201
|
|
|
? 'Kint_Decorators_Rich' |
202
|
|
|
: 'Kint_Decorators_Plain'; |
203
|
|
|
|
204
|
|
|
$output = ''; |
205
|
|
|
if ( self::$_firstRun ) { |
206
|
|
|
$output .= call_user_func( array( $decorator, 'init' ) ); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
|
210
|
|
|
$trace = false; |
211
|
|
|
if ( $names === array( null ) && func_num_args() === 1 && $data === 1 ) { # Kint::dump(1) shorthand |
212
|
|
|
$trace = KINT_PHP53 ? debug_backtrace( true ) : debug_backtrace(); |
213
|
|
|
} elseif ( func_num_args() === 1 && is_array( $data ) ) { |
214
|
|
|
$trace = $data; # test if the single parameter is result of debug_backtrace() |
215
|
|
|
} |
216
|
|
|
$trace and $trace = self::_parseTrace( $trace ); |
|
|
|
|
217
|
|
|
|
218
|
|
|
|
219
|
|
|
$output .= call_user_func( array( $decorator, 'wrapStart' ) ); |
220
|
|
|
if ( $trace ) { |
221
|
|
|
$output .= call_user_func( array( $decorator, 'decorateTrace' ), $trace ); |
222
|
|
|
} else { |
223
|
|
|
$data = func_num_args() === 0 |
224
|
|
|
? array( "[[no arguments passed]]" ) |
225
|
|
|
: func_get_args(); |
226
|
|
|
|
227
|
|
|
foreach ( $data as $k => $argument ) { |
228
|
|
|
kintParser::reset(); |
229
|
|
|
# when the dump arguments take long to generate output, user might have changed the file and |
230
|
|
|
# Kint might not parse the arguments correctly, so check if names are set and while the |
231
|
|
|
# displayed names might be wrong, at least don't throw an error |
232
|
|
|
$output .= call_user_func( |
233
|
|
|
array( $decorator, 'decorate' ), |
234
|
|
|
kintParser::factory( $argument, isset( $names[ $k ] ) ? $names[ $k ] : '' ) |
235
|
|
|
); |
236
|
|
|
} |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
$output .= call_user_func( array( $decorator, 'wrapEnd' ), $callee, $miniTrace, $previousCaller ); |
240
|
|
|
|
241
|
|
|
self::enabled( $modeOldValue ); |
242
|
|
|
|
243
|
|
|
self::$_firstRun = false; |
244
|
|
|
if ( strpos( $modifiers, '~' ) !== false ) { |
245
|
|
|
self::$_firstRun = $firstRunOldValue; |
246
|
|
|
} else { |
247
|
|
|
self::enabled( $modeOldValue ); |
248
|
|
|
} |
249
|
|
|
if ( strpos( $modifiers, '!' ) !== false ) { |
250
|
|
|
self::$expandedByDefault = $expandedByDefaultOldValue; |
|
|
|
|
251
|
|
|
} |
252
|
|
|
if ( strpos( $modifiers, '+' ) !== false ) { |
253
|
|
|
self::$maxLevels = $maxLevelsOldValue; |
|
|
|
|
254
|
|
|
} |
255
|
|
|
if ( strpos( $modifiers, '@' ) !== false ) { |
256
|
|
|
self::$returnOutput = $returnOldValue; |
|
|
|
|
257
|
|
|
self::$_firstRun = $firstRunOldValue; |
258
|
|
|
return $output; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
if ( self::$returnOutput ) return $output; |
262
|
|
|
|
263
|
|
|
echo $output; |
264
|
|
|
return ''; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* generic path display callback, can be configured in the settings; purpose is to show relevant path info and hide |
270
|
|
|
* as much of the path as possible. |
271
|
|
|
* |
272
|
|
|
* @param string $file |
273
|
|
|
* |
274
|
|
|
* @return string |
275
|
|
|
*/ |
276
|
|
|
public static function shortenPath( $file ) |
277
|
|
|
{ |
278
|
|
|
$file = str_replace( '\\', '/', $file ); |
279
|
|
|
$shortenedName = $file; |
280
|
|
|
$replaced = false; |
281
|
|
|
if ( is_array( self::$appRootDirs ) ) foreach ( self::$appRootDirs as $path => $replaceString ) { |
282
|
|
|
if ( empty( $path ) ) continue; |
283
|
|
|
|
284
|
|
|
$path = str_replace( '\\', '/', $path ); |
285
|
|
|
|
286
|
|
|
if ( strpos( $file, $path ) === 0 ) { |
287
|
|
|
$shortenedName = $replaceString . substr( $file, strlen( $path ) ); |
288
|
|
|
$replaced = true; |
289
|
|
|
break; |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
# fallback to find common path with Kint dir |
294
|
|
|
if ( !$replaced ) { |
295
|
|
|
$pathParts = explode( '/', str_replace( '\\', '/', KINT_DIR ) ); |
296
|
|
|
$fileParts = explode( '/', $file ); |
297
|
|
|
$i = 0; |
298
|
|
|
foreach ( $fileParts as $i => $filePart ) { |
299
|
|
|
if ( !isset( $pathParts[ $i ] ) || $pathParts[ $i ] !== $filePart ) break; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
$shortenedName = ( $i ? '.../' : '' ) . implode( '/', array_slice( $fileParts, $i ) ); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
return $shortenedName; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
public static function getIdeLink( $file, $line ) |
|
|
|
|
309
|
|
|
{ |
310
|
|
|
return str_replace( array( '%f', '%l' ), array( $file, $line ), self::$fileLinkFormat ); |
311
|
|
|
} |
312
|
|
|
|
313
|
|
|
/** |
314
|
|
|
* trace helper, shows the place in code inline |
315
|
|
|
* |
316
|
|
|
* @param string $file full path to file |
317
|
|
|
* @param int $lineNumber the line to display |
318
|
|
|
* @param int $padding surrounding lines to show besides the main one |
319
|
|
|
* |
320
|
|
|
* @return bool|string |
321
|
|
|
*/ |
322
|
|
|
private static function _showSource( $file, $lineNumber, $padding = 7 ) |
323
|
|
|
{ |
324
|
|
|
if ( !$file OR !is_readable( $file ) ) { |
|
|
|
|
325
|
|
|
# continuing will cause errors |
326
|
|
|
return false; |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
# open the file and set the line position |
330
|
|
|
$file = fopen( $file, 'r' ); |
331
|
|
|
$line = 0; |
332
|
|
|
|
333
|
|
|
# Set the reading range |
334
|
|
|
$range = array( |
335
|
|
|
'start' => $lineNumber - $padding, |
336
|
|
|
'end' => $lineNumber + $padding |
337
|
|
|
); |
338
|
|
|
|
339
|
|
|
# set the zero-padding amount for line numbers |
340
|
|
|
$format = '% ' . strlen( $range['end'] ) . 'd'; |
341
|
|
|
|
342
|
|
|
$source = ''; |
343
|
|
|
while ( ( $row = fgets( $file ) ) !== false ) { |
344
|
|
|
# increment the line number |
345
|
|
|
if ( ++$line > $range['end'] ) { |
346
|
|
|
break; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
|
if ( $line >= $range['start'] ) { |
350
|
|
|
# make the row safe for output |
351
|
|
|
$row = htmlspecialchars( $row, ENT_NOQUOTES, 'UTF-8' ); |
352
|
|
|
|
353
|
|
|
# trim whitespace and sanitize the row |
354
|
|
|
$row = '<span>' . sprintf( $format, $line ) . '</span> ' . $row; |
355
|
|
|
|
356
|
|
|
if ( $line === $lineNumber ) { |
357
|
|
|
# apply highlighting to this row |
358
|
|
|
$row = '<div class="kint-highlight">' . $row . '</div>'; |
359
|
|
|
} else { |
360
|
|
|
$row = '<div>' . $row . '</div>'; |
361
|
|
|
} |
362
|
|
|
|
363
|
|
|
# add to the captured source |
364
|
|
|
$source .= $row; |
365
|
|
|
} |
366
|
|
|
} |
367
|
|
|
|
368
|
|
|
# close the file |
369
|
|
|
fclose( $file ); |
370
|
|
|
|
371
|
|
|
return $source; |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
|
375
|
|
|
/** |
376
|
|
|
* returns parameter names that the function was passed, as well as any predefined symbols before function |
377
|
|
|
* call (modifiers) |
378
|
|
|
* |
379
|
|
|
* @param array $trace |
380
|
|
|
* |
381
|
|
|
* @return array( $parameters, $modifier, $callee, $previousCaller ) |
|
|
|
|
382
|
|
|
*/ |
383
|
|
|
private static function _getCalleeInfo( $trace ) |
384
|
|
|
{ |
385
|
|
|
$previousCaller = array(); |
386
|
|
|
$miniTrace = array(); |
387
|
|
|
$prevStep = array(); |
388
|
|
|
|
389
|
|
|
# go from back of trace to find first occurrence of call to Kint or its wrappers |
390
|
|
|
while ( $step = array_pop( $trace ) ) { |
391
|
|
|
|
392
|
|
|
if ( self::_stepIsInternal( $step ) ) { |
393
|
|
|
$previousCaller = $prevStep; |
394
|
|
|
break; |
395
|
|
|
} elseif ( isset( $step['file'], $step['line'] ) ) { |
396
|
|
|
unset( $step['object'], $step['args'] ); |
397
|
|
|
array_unshift( $miniTrace, $step ); |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
$prevStep = $step; |
401
|
|
|
} |
402
|
|
|
$callee = $step; |
403
|
|
|
|
404
|
|
|
if ( !isset( $callee['file'] ) || !is_readable( $callee['file'] ) ) return false; |
405
|
|
|
|
406
|
|
|
|
407
|
|
|
# open the file and read it up to the position where the function call expression ended |
408
|
|
|
$file = fopen( $callee['file'], 'r' ); |
409
|
|
|
$line = 0; |
410
|
|
|
$source = ''; |
411
|
|
|
while ( ( $row = fgets( $file ) ) !== false ) { |
412
|
|
|
if ( ++$line > $callee['line'] ) break; |
413
|
|
|
$source .= $row; |
414
|
|
|
} |
415
|
|
|
fclose( $file ); |
416
|
|
|
$source = self::_removeAllButCode( $source ); |
417
|
|
|
|
418
|
|
|
|
419
|
|
|
if ( empty( $callee['class'] ) ) { |
420
|
|
|
$codePattern = $callee['function']; |
421
|
|
|
} else { |
422
|
|
|
if ( $callee['type'] === '::' ) { |
423
|
|
|
$codePattern = $callee['class'] . "\x07*" . $callee['type'] . "\x07*" . $callee['function'];; |
424
|
|
|
} else /*if ( $callee['type'] === '->' )*/ { |
425
|
|
|
$codePattern = ".*\x07*" . $callee['type'] . "\x07*" . $callee['function'];; |
426
|
|
|
} |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
// todo if more than one call in one line - not possible to determine variable names |
430
|
|
|
// todo does not recognize string concat |
431
|
|
|
# get the position of the last call to the function |
432
|
|
|
preg_match_all( " |
433
|
|
|
[ |
434
|
|
|
# beginning of statement |
435
|
|
|
[\x07{(] |
436
|
|
|
|
437
|
|
|
# search for modifiers (group 1) |
438
|
|
|
([-+!@~]*)? |
439
|
|
|
|
440
|
|
|
# spaces |
441
|
|
|
\x07* |
442
|
|
|
|
443
|
|
|
# check if output is assigned to a variable (group 2) todo: does not detect concat |
444
|
|
|
( |
445
|
|
|
\\$[a-z0-9_]+ # variable |
446
|
|
|
\x07*\\.?=\x07* # assignment |
447
|
|
|
)? |
448
|
|
|
|
449
|
|
|
# possibly a namespace symbol |
450
|
|
|
\\\\? |
451
|
|
|
|
452
|
|
|
# spaces again |
453
|
|
|
\x07* |
454
|
|
|
|
455
|
|
|
# main call to Kint |
456
|
|
|
{$codePattern} |
457
|
|
|
|
458
|
|
|
# spaces everywhere |
459
|
|
|
\x07* |
460
|
|
|
|
461
|
|
|
# find the character where kint's opening bracket resides (group 3) |
462
|
|
|
(\\() |
463
|
|
|
|
464
|
|
|
]ix", |
465
|
|
|
$source, |
466
|
|
|
$matches, |
467
|
|
|
PREG_OFFSET_CAPTURE |
468
|
|
|
); |
469
|
|
|
|
470
|
|
|
$modifiers = end( $matches[1] ); |
471
|
|
|
$assignment = end( $matches[2] ); |
472
|
|
|
$bracket = end( $matches[3] ); |
473
|
|
|
|
474
|
|
|
$modifiers = $modifiers[0]; |
475
|
|
|
if ( $assignment[1] !== -1 ) { |
476
|
|
|
$modifiers .= '@'; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
$paramsString = preg_replace( "[\x07+]", ' ', substr( $source, $bracket[1] + 1 ) ); |
480
|
|
|
# we now have a string like this: |
481
|
|
|
# <parameters passed>); <the rest of the last read line> |
482
|
|
|
|
483
|
|
|
# remove everything in brackets and quotes, we don't need nested statements nor literal strings which would |
484
|
|
|
# only complicate separating individual arguments |
485
|
|
|
$c = strlen( $paramsString ); |
486
|
|
|
$inString = $escaped = $openedBracket = $closingBracket = false; |
487
|
|
|
$i = 0; |
488
|
|
|
$inBrackets = 0; |
489
|
|
|
$openedBrackets = array(); |
490
|
|
|
|
491
|
|
|
while ( $i < $c ) { |
492
|
|
|
$letter = $paramsString[ $i ]; |
493
|
|
|
|
494
|
|
|
if ( !$inString ) { |
495
|
|
|
if ( $letter === '\'' || $letter === '"' ) { |
496
|
|
|
$inString = $letter; |
497
|
|
|
} elseif ( $letter === '(' || $letter === '[' ) { |
498
|
|
|
$inBrackets++; |
499
|
|
|
$openedBrackets[] = $openedBracket = $letter; |
500
|
|
|
$closingBracket = $openedBracket === '(' ? ')' : ']'; |
501
|
|
|
} elseif ( $inBrackets && $letter === $closingBracket ) { |
502
|
|
|
$inBrackets--; |
503
|
|
|
array_pop( $openedBrackets ); |
504
|
|
|
$openedBracket = end( $openedBrackets ); |
505
|
|
|
$closingBracket = $openedBracket === '(' ? ')' : ']'; |
506
|
|
|
} elseif ( !$inBrackets && $letter === ')' ) { |
507
|
|
|
$paramsString = substr( $paramsString, 0, $i ); |
508
|
|
|
break; |
509
|
|
|
} |
510
|
|
|
} elseif ( $letter === $inString && !$escaped ) { |
511
|
|
|
$inString = false; |
512
|
|
|
} |
513
|
|
|
|
514
|
|
|
# replace whatever was inside quotes or brackets with untypeable characters, we don't |
515
|
|
|
# need that info. We'll later replace the whole string with '...' |
516
|
|
|
if ( $inBrackets > 0 ) { |
517
|
|
|
if ( $inBrackets > 1 || $letter !== $openedBracket ) { |
518
|
|
|
$paramsString[ $i ] = "\x07"; |
519
|
|
|
} |
520
|
|
|
} |
521
|
|
|
if ( $inString ) { |
522
|
|
|
if ( $letter !== $inString || $escaped ) { |
523
|
|
|
$paramsString[ $i ] = "\x07"; |
524
|
|
|
} |
525
|
|
|
} |
526
|
|
|
|
527
|
|
|
$escaped = !$escaped && ( $letter === '\\' ); |
528
|
|
|
$i++; |
529
|
|
|
} |
530
|
|
|
|
531
|
|
|
# by now we have an un-nested arguments list, lets make it to an array for processing further |
532
|
|
|
$arguments = explode( ',', preg_replace( "[\x07+]", '...', $paramsString ) ); |
533
|
|
|
|
534
|
|
|
# test each argument whether it was passed literary or was it an expression or a variable name |
535
|
|
|
$parameters = array(); |
536
|
|
|
$blacklist = array( 'null', 'true', 'false', 'array(...)', 'array()', '"..."', '[...]', 'b"..."', ); |
537
|
|
|
foreach ( $arguments as $argument ) { |
538
|
|
|
$argument = trim( $argument ); |
539
|
|
|
|
540
|
|
|
if ( is_numeric( $argument ) |
541
|
|
|
|| in_array( str_replace( "'", '"', strtolower( $argument ) ), $blacklist, true ) |
542
|
|
|
) { |
543
|
|
|
$parameters[] = null; |
544
|
|
|
} else { |
545
|
|
|
$parameters[] = $argument; |
546
|
|
|
} |
547
|
|
|
} |
548
|
|
|
|
549
|
|
|
return array( $parameters, $modifiers, $callee, $previousCaller, $miniTrace ); |
550
|
|
|
} |
551
|
|
|
|
552
|
|
|
/** |
553
|
|
|
* removes comments and zaps whitespace & <?php tags from php code, makes for easier further parsing |
554
|
|
|
* |
555
|
|
|
* @param string $source |
556
|
|
|
* |
557
|
|
|
* @return string |
558
|
|
|
*/ |
559
|
|
|
private static function _removeAllButCode( $source ) |
560
|
|
|
{ |
561
|
|
|
$commentTokens = array( |
562
|
|
|
T_COMMENT => true, T_INLINE_HTML => true, T_DOC_COMMENT => true |
563
|
|
|
); |
564
|
|
|
$whiteSpaceTokens = array( |
565
|
|
|
T_WHITESPACE => true, T_CLOSE_TAG => true, |
566
|
|
|
T_OPEN_TAG => true, T_OPEN_TAG_WITH_ECHO => true, |
567
|
|
|
); |
568
|
|
|
|
569
|
|
|
$cleanedSource = ''; |
570
|
|
|
foreach ( token_get_all( $source ) as $token ) { |
571
|
|
|
if ( is_array( $token ) ) { |
572
|
|
|
if ( isset( $commentTokens[ $token[0] ] ) ) continue; |
573
|
|
|
|
574
|
|
|
if ( isset( $whiteSpaceTokens[ $token[0] ] ) ) { |
575
|
|
|
$token = "\x07"; |
576
|
|
|
} else { |
577
|
|
|
$token = $token[1]; |
578
|
|
|
} |
579
|
|
|
} elseif ( $token === ';' ) { |
580
|
|
|
$token = "\x07"; |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
$cleanedSource .= $token; |
584
|
|
|
} |
585
|
|
|
return $cleanedSource; |
586
|
|
|
} |
587
|
|
|
|
588
|
|
|
/** |
589
|
|
|
* returns whether current trace step belongs to Kint or its wrappers |
590
|
|
|
* |
591
|
|
|
* @param $step |
592
|
|
|
* |
593
|
|
|
* @return array |
|
|
|
|
594
|
|
|
*/ |
595
|
|
|
private static function _stepIsInternal( $step ) |
596
|
|
|
{ |
597
|
|
|
if ( isset( $step['class'] ) ) { |
598
|
|
|
foreach ( self::$aliases['methods'] as $alias ) { |
599
|
|
|
if ( $alias[0] === strtolower( $step['class'] ) && $alias[1] === strtolower( $step['function'] ) ) { |
600
|
|
|
return true; |
601
|
|
|
} |
602
|
|
|
} |
603
|
|
|
return false; |
604
|
|
|
} else { |
605
|
|
|
return in_array( strtolower( $step['function'] ), self::$aliases['functions'], true ); |
606
|
|
|
} |
607
|
|
|
} |
608
|
|
|
|
609
|
|
|
private static function _parseTrace( array $data ) |
610
|
|
|
{ |
611
|
|
|
$trace = array(); |
612
|
|
|
$traceFields = array( 'file', 'line', 'args', 'class' ); |
613
|
|
|
$fileFound = false; # file element must exist in one of the steps |
614
|
|
|
|
615
|
|
|
# validate whether a trace was indeed passed |
616
|
|
|
while ( $step = array_pop( $data ) ) { |
617
|
|
|
if ( !is_array( $step ) || !isset( $step['function'] ) ) return false; |
618
|
|
|
if ( !$fileFound && isset( $step['file'] ) && file_exists( $step['file'] ) ) { |
619
|
|
|
$fileFound = true; |
620
|
|
|
} |
621
|
|
|
|
622
|
|
|
$valid = false; |
623
|
|
|
foreach ( $traceFields as $element ) { |
624
|
|
|
if ( isset( $step[ $element ] ) ) { |
625
|
|
|
$valid = true; |
626
|
|
|
break; |
627
|
|
|
} |
628
|
|
|
} |
629
|
|
|
if ( !$valid ) return false; |
630
|
|
|
|
631
|
|
|
if ( self::_stepIsInternal( $step ) ) { |
632
|
|
|
$step = array( |
633
|
|
|
'file' => $step['file'], |
634
|
|
|
'line' => $step['line'], |
635
|
|
|
'function' => '', |
636
|
|
|
); |
637
|
|
|
array_unshift( $trace, $step ); |
638
|
|
|
break; |
639
|
|
|
} |
640
|
|
|
if ( $step['function'] !== 'spl_autoload_call' ) { # meaningless |
641
|
|
|
array_unshift( $trace, $step ); |
642
|
|
|
} |
643
|
|
|
} |
644
|
|
|
if ( !$fileFound ) return false; |
645
|
|
|
|
646
|
|
|
$output = array(); |
647
|
|
|
foreach ( $trace as $step ) { |
648
|
|
|
if ( isset( $step['file'] ) ) { |
649
|
|
|
$file = $step['file']; |
650
|
|
|
|
651
|
|
|
if ( isset( $step['line'] ) ) { |
652
|
|
|
$line = $step['line']; |
653
|
|
|
# include the source of this step |
654
|
|
|
if ( self::enabled() === self::MODE_RICH ) { |
655
|
|
|
$source = self::_showSource( $file, $line ); |
656
|
|
|
} |
657
|
|
|
} |
658
|
|
|
} |
659
|
|
|
|
660
|
|
|
$function = $step['function']; |
661
|
|
|
|
662
|
|
|
if ( in_array( $function, array( 'include', 'include_once', 'require', 'require_once' ) ) ) { |
663
|
|
|
if ( empty( $step['args'] ) ) { |
664
|
|
|
# no arguments |
665
|
|
|
$args = array(); |
666
|
|
|
} else { |
667
|
|
|
# sanitize the included file path |
668
|
|
|
$args = array( 'file' => self::shortenPath( $step['args'][0] ) ); |
669
|
|
|
} |
670
|
|
|
} elseif ( isset( $step['args'] ) ) { |
671
|
|
|
if ( empty( $step['class'] ) && !function_exists( $function ) ) { |
672
|
|
|
# introspection on closures or language constructs in a stack trace is impossible before PHP 5.3 |
673
|
|
|
$params = null; |
674
|
|
|
} else { |
675
|
|
|
try { |
676
|
|
|
if ( isset( $step['class'] ) ) { |
677
|
|
|
if ( method_exists( $step['class'], $function ) ) { |
678
|
|
|
$reflection = new ReflectionMethod( $step['class'], $function ); |
679
|
|
|
} else if ( isset( $step['type'] ) && $step['type'] == '::' ) { |
680
|
|
|
$reflection = new ReflectionMethod( $step['class'], '__callStatic' ); |
681
|
|
|
} else { |
682
|
|
|
$reflection = new ReflectionMethod( $step['class'], '__call' ); |
683
|
|
|
} |
684
|
|
|
} else { |
685
|
|
|
$reflection = new ReflectionFunction( $function ); |
686
|
|
|
} |
687
|
|
|
|
688
|
|
|
# get the function parameters |
689
|
|
|
$params = $reflection->getParameters(); |
690
|
|
|
} catch ( Exception $e ) { # avoid various PHP version incompatibilities |
691
|
|
|
$params = null; |
692
|
|
|
} |
693
|
|
|
} |
694
|
|
|
|
695
|
|
|
$args = array(); |
696
|
|
|
foreach ( $step['args'] as $i => $arg ) { |
697
|
|
|
if ( isset( $params[ $i ] ) ) { |
698
|
|
|
# assign the argument by the parameter name |
699
|
|
|
$args[ $params[ $i ]->name ] = $arg; |
700
|
|
|
} else { |
701
|
|
|
# assign the argument by number |
702
|
|
|
$args[ '#' . ( $i + 1 ) ] = $arg; |
703
|
|
|
} |
704
|
|
|
} |
705
|
|
|
} |
706
|
|
|
|
707
|
|
|
if ( isset( $step['class'] ) ) { |
708
|
|
|
# Class->method() or Class::method() |
709
|
|
|
$function = $step['class'] . $step['type'] . $function; |
710
|
|
|
} |
711
|
|
|
|
712
|
|
|
// todo it's possible to parse the object name out from the source! |
713
|
|
|
$output[] = array( |
714
|
|
|
'function' => $function, |
715
|
|
|
'args' => isset( $args ) ? $args : null, |
716
|
|
|
'file' => isset( $file ) ? $file : null, |
717
|
|
|
'line' => isset( $line ) ? $line : null, |
718
|
|
|
'source' => isset( $source ) ? $source : null, |
719
|
|
|
'object' => isset( $step['object'] ) ? $step['object'] : null, |
720
|
|
|
); |
721
|
|
|
|
722
|
|
|
unset( $function, $args, $file, $line, $source ); |
723
|
|
|
} |
724
|
|
|
|
725
|
|
|
return $output; |
726
|
|
|
} |
727
|
|
|
} |
728
|
|
|
|
729
|
|
|
|
730
|
|
View Code Duplication |
if ( !function_exists( 'd' ) ) { |
731
|
|
|
/** |
732
|
|
|
* Alias of Kint::dump() |
733
|
|
|
* |
734
|
|
|
* @return string |
735
|
|
|
*/ |
736
|
|
|
function d() |
737
|
|
|
{ |
738
|
|
|
if ( !Kint::enabled() ) return ''; |
739
|
|
|
$_ = func_get_args(); |
740
|
|
|
return call_user_func_array( array( 'Kint', 'dump' ), $_ ); |
741
|
|
|
} |
742
|
|
|
} |
743
|
|
|
|
744
|
|
View Code Duplication |
if ( !function_exists( 'dd' ) ) { |
745
|
|
|
/** |
746
|
|
|
* Alias of Kint::dump() |
747
|
|
|
* [!!!] IMPORTANT: execution will halt after call to this function |
748
|
|
|
* |
749
|
|
|
* @return string |
|
|
|
|
750
|
|
|
* @deprecated |
751
|
|
|
*/ |
752
|
|
|
function dd() |
753
|
|
|
{ |
754
|
|
|
if ( !Kint::enabled() ) return ''; |
755
|
|
|
|
756
|
|
|
echo "<pre>Kint: dd() is being deprecated, please use ddd() instead</pre>\n"; |
757
|
|
|
$_ = func_get_args(); |
758
|
|
|
call_user_func_array( array( 'Kint', 'dump' ), $_ ); |
759
|
|
|
die; |
|
|
|
|
760
|
|
|
} |
761
|
|
|
} |
762
|
|
|
|
763
|
|
View Code Duplication |
if ( !function_exists( 'ddd' ) ) { |
764
|
|
|
/** |
765
|
|
|
* Alias of Kint::dump() |
766
|
|
|
* [!!!] IMPORTANT: execution will halt after call to this function |
767
|
|
|
* |
768
|
|
|
* @return string |
|
|
|
|
769
|
|
|
*/ |
770
|
|
|
function ddd() |
771
|
|
|
{ |
772
|
|
|
if ( !Kint::enabled() ) return ''; |
773
|
|
|
$_ = func_get_args(); |
774
|
|
|
call_user_func_array( array( 'Kint', 'dump' ), $_ ); |
775
|
|
|
die; |
|
|
|
|
776
|
|
|
} |
777
|
|
|
} |
778
|
|
|
|
779
|
|
|
if ( !function_exists( 's' ) ) { |
780
|
|
|
/** |
781
|
|
|
* Alias of Kint::dump(), however the output is in plain htmlescaped text and some minor visibility enhancements |
782
|
|
|
* added. If run in CLI mode, output is pure whitespace. |
783
|
|
|
* |
784
|
|
|
* To force rendering mode without autodetecting anything: |
785
|
|
|
* |
786
|
|
|
* Kint::enabled( Kint::MODE_PLAIN ); |
787
|
|
|
* Kint::dump( $variable ); |
788
|
|
|
* |
789
|
|
|
* [!!!] IMPORTANT: execution will halt after call to this function |
790
|
|
|
* |
791
|
|
|
* @return string |
792
|
|
|
*/ |
793
|
|
|
function s() |
794
|
|
|
{ |
795
|
|
|
$enabled = Kint::enabled(); |
796
|
|
|
if ( !$enabled ) return ''; |
797
|
|
|
|
798
|
|
|
if ( $enabled === Kint::MODE_WHITESPACE ) { # if already in whitespace, don't elevate to plain |
799
|
|
|
$restoreMode = Kint::MODE_WHITESPACE; |
800
|
|
|
} else { |
801
|
|
|
$restoreMode = Kint::enabled( # remove cli colors in cli mode; remove rich interface in HTML mode |
802
|
|
|
PHP_SAPI === 'cli' ? Kint::MODE_WHITESPACE : Kint::MODE_PLAIN |
803
|
|
|
); |
804
|
|
|
} |
805
|
|
|
|
806
|
|
|
$params = func_get_args(); |
807
|
|
|
$dump = call_user_func_array( array( 'Kint', 'dump' ), $params ); |
808
|
|
|
Kint::enabled( $restoreMode ); |
809
|
|
|
return $dump; |
810
|
|
|
} |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
if ( !function_exists( 'sd' ) ) { |
814
|
|
|
/** |
815
|
|
|
* @see s() |
816
|
|
|
* |
817
|
|
|
* [!!!] IMPORTANT: execution will halt after call to this function |
818
|
|
|
* |
819
|
|
|
* @return string |
|
|
|
|
820
|
|
|
*/ |
821
|
|
|
function sd() |
822
|
|
|
{ |
823
|
|
|
$enabled = Kint::enabled(); |
824
|
|
|
if ( !$enabled ) return ''; |
825
|
|
|
|
826
|
|
|
if ( $enabled !== Kint::MODE_WHITESPACE ) { |
827
|
|
|
Kint::enabled( |
828
|
|
|
PHP_SAPI === 'cli' ? Kint::MODE_WHITESPACE : Kint::MODE_PLAIN |
829
|
|
|
); |
830
|
|
|
} |
831
|
|
|
|
832
|
|
|
$params = func_get_args(); |
833
|
|
|
call_user_func_array( array( 'Kint', 'dump' ), $params ); |
834
|
|
|
die; |
|
|
|
|
835
|
|
|
} |
836
|
|
|
} |
837
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.