Completed
Push — next ( 3fc9f0...5d2fc6 )
by Jonathan
9s
created

Kint.php (17 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
use Kint\CallFinder;
4
use Kint\Object\BasicObject;
5
use Kint\Object\NothingObject;
6
use Kint\Parser\Parser;
7
use Kint\Parser\Plugin;
8
use Kint\Parser\TracePlugin;
9
10
class Kint
0 ignored issues
show
The property $enabled_mode is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
The property $mode_default is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
The property $mode_default_cli is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
The property $file_link_format is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
The property $display_called_from is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
The property $app_root_dirs is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
The property $max_depth is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
The property $cli_detection is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
The property $plugin_pool is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
The property $dump_array is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
11
{
12
    /**
13
     * @var mixed Kint mode
14
     *
15
     * false: Disabled
16
     * true: Enabled, default mode selection
17
     * other: Manual mode selection
18
     */
19
    public static $enabled_mode = true;
20
21
    /**
22
     * Default mode.
23
     *
24
     * @var string
25
     */
26
    public static $mode_default = self::MODE_RICH;
27
28
    /**
29
     * Default mode in CLI with cli_detection on.
30
     *
31
     * @var string
32
     */
33
    public static $mode_default_cli = self::MODE_CLI;
34
35
    /**
36
     * @var bool Return output instead of echoing
37
     */
38
    public static $return;
39
40
    /**
41
     * @var string format of the link to the source file in trace entries.
42
     *
43
     * Use %f for file path, %l for line number.
44
     *
45
     * [!] EXAMPLE (works with for phpStorm and RemoteCall Plugin):
46
     *
47
     * Kint::$file_link_format = 'http://localhost:8091/?message=%f:%l';
48
     */
49
    public static $file_link_format = '';
50
51
    /**
52
     * @var bool whether to display where kint was called from
53
     */
54
    public static $display_called_from = true;
55
56
    /**
57
     * @var array base directories of your application that will be displayed instead of the full path.
58
     *
59
     * Keys are paths, values are replacement strings
60
     *
61
     * [!] EXAMPLE (for Laravel 5):
62
     *
63
     * Kint::$app_root_dirs = [
64
     *     base_path() => '<BASE>',
65
     *     app_path() => '<APP>',
66
     *     config_path() => '<CONFIG>',
67
     *     database_path() => '<DATABASE>',
68
     *     public_path() => '<PUBLIC>',
69
     *     resource_path() => '<RESOURCE>',
70
     *     storage_path() => '<STORAGE>',
71
     * ];
72
     *
73
     * Defaults to [$_SERVER['DOCUMENT_ROOT'] => '<ROOT>']
74
     */
75
    public static $app_root_dirs = array();
76
77
    /**
78
     * @var int max array/object levels to go deep, if zero no limits are applied
79
     */
80
    public static $max_depth = 6;
81
82
    /**
83
     * @var bool expand all trees by default for rich view
84
     */
85
    public static $expanded = false;
86
87
    /**
88
     * @var bool enable detection when Kint is command line.
89
     *
90
     * Formats output with whitespace only; does not HTML-escape it
91
     */
92
    public static $cli_detection = true;
93
94
    /**
95
     * @var array Kint aliases. Add debug functions in Kint wrappers here to fix modifiers and backtraces
96
     */
97
    public static $aliases = array(
98
        array('Kint', 'dump'),
99
        array('Kint', 'trace'),
100
        array('Kint', 'dumpArray'),
101
    );
102
103
    /**
104
     * @var array Kint\Renderer\Renderer descendants. Add to array to extend.
105
     */
106
    public static $renderers = array(
107
        self::MODE_RICH => 'Kint\\Renderer\\RichRenderer',
108
        self::MODE_PLAIN => 'Kint\\Renderer\\PlainRenderer',
109
        self::MODE_TEXT => 'Kint\\Renderer\\TextRenderer',
110
        self::MODE_CLI => 'Kint\\Renderer\\CliRenderer',
111
    );
112
113
    const MODE_RICH = 'r';
114
    const MODE_TEXT = 't';
115
    const MODE_CLI = 'c';
116
    const MODE_PLAIN = 'p';
117
118
    public static $plugins = array(
119
        'Kint\\Parser\\ArrayObjectPlugin',
120
        'Kint\\Parser\\Base64Plugin',
121
        'Kint\\Parser\\BlacklistPlugin',
122
        'Kint\\Parser\\ClassMethodsPlugin',
123
        'Kint\\Parser\\ClassStaticsPlugin',
124
        'Kint\\Parser\\ClosurePlugin',
125
        'Kint\\Parser\\ColorPlugin',
126
        'Kint\\Parser\\DateTimePlugin',
127
        'Kint\\Parser\\FsPathPlugin',
128
        'Kint\\Parser\\IteratorPlugin',
129
        'Kint\\Parser\\JsonPlugin',
130
        'Kint\\Parser\\MicrotimePlugin',
131
        'Kint\\Parser\\SimpleXMLElementPlugin',
132
        'Kint\\Parser\\SplFileInfoPlugin',
133
        'Kint\\Parser\\SplObjectStoragePlugin',
134
        'Kint\\Parser\\StreamPlugin',
135
        'Kint\\Parser\\TablePlugin',
136
        'Kint\\Parser\\ThrowablePlugin',
137
        'Kint\\Parser\\TimestampPlugin',
138
        'Kint\\Parser\\ToStringPlugin',
139
        'Kint\\Parser\\TracePlugin',
140
        'Kint\\Parser\\XmlPlugin',
141
    );
142
143
    private static $plugin_pool = array();
144
    private static $dump_array = false;
145
    private static $names = array();
146
147
    /**
148
     * Stashes or sets all settings at once.
149
     *
150
     * @param array|null $settings Array of all settings to be set or null to set none
151
     *
152
     * @return array Current settings
153
     */
154
    public static function settings(array $settings = null)
155
    {
156
        static $keys = array(
157
            'aliases',
158
            'app_root_dirs',
159
            'cli_detection',
160
            'display_called_from',
161
            'enabled_mode',
162
            'expanded',
163
            'file_link_format',
164
            'max_depth',
165
            'mode_default',
166
            'mode_default_cli',
167
            'renderers',
168
            'return',
169
            'plugins',
170
        );
171
172
        $out = array();
173
174
        foreach ($keys as $key) {
175
            $out[$key] = self::$$key;
176
        }
177
178
        if ($settings !== null) {
179
            $in = array_intersect_key($settings, $out);
180
            foreach ($in as $key => $val) {
181
                self::$$key = $val;
182
            }
183
        }
184
185
        return $out;
186
    }
187
188
    /**
189
     * Prints a debug backtrace, same as Kint::dump(1).
190
     *
191
     * @param array $trace [OPTIONAL] you can pass your own trace, otherwise, `debug_backtrace` will be called
192
     *
193
     * @return mixed
194
     */
195
    public static function trace(array $trace = null)
196
    {
197
        if ($trace === null) {
198
            $trace = debug_backtrace(true);
0 ignored issues
show
Consider using a different name than the parameter $trace. This often makes code more readable.
Loading history...
199
        } else {
200
            return self::dump($trace);
201
        }
202
203
        TracePlugin::normalizeAliases(self::$aliases);
204
205
        $trimmed_trace = array();
206
207
        foreach ($trace as $frame) {
208
            if (TracePlugin::frameIsListed($frame, self::$aliases)) {
209
                $trimmed_trace = array();
210
            }
211
212
            $trimmed_trace[] = $frame;
213
        }
214
215
        return self::dumpArray(
216
            array($trimmed_trace),
217
            array(BasicObject::blank('Kint::trace()', 'debug_backtrace()'))
218
        );
219
    }
220
221
    /**
222
     * Dumps an array as separate values, and uses $names to seed the parser.
223
     *
224
     * @param array              $data  Data to be dumped
225
     * @param BasicObject[]|null $names Array of BasicObject to seed the parser with
226
     */
227
    public static function dumpArray(array $data, array $names = null)
228
    {
229
        self::$names = $names;
230
        self::$dump_array = true;
231
232
        $out = self::dump($data);
233
234
        self::$names = null;
235
        self::$dump_array = false;
236
237
        return $out;
238
    }
239
240
    /**
241
     * Dump information about variables, accepts any number of parameters, supports modifiers:.
242
     *
243
     *  clean up any output before kint and place the dump at the top of page:
244
     *   - Kint::dump()
245
     *  *****
246
     *  expand all nodes on display:
247
     *   ! Kint::dump()
248
     *  *****
249
     *  dump variables disregarding their depth:
250
     *   + Kint::dump()
251
     *  *****
252
     *  return output instead of displaying it:
253
     *
254
     *   @ Kint::dump()
255
     *  *****
256
     *  force output as plain text
257
     *   ~ Kint::dump()
258
     *
259
     * Modifiers are supported by all dump wrapper functions, including Kint::trace(). Space is optional.
260
     *
261
     * @param mixed $data
262
     *
263
     * @return int|string
264
     */
265
    public static function dump($data = null)
266
    {
267
        if (!self::$enabled_mode) {
268
            return 0;
269
        }
270
271
        $stash = self::settings();
272
        $num_args = func_num_args();
273
274
        list($params, $modifiers, $callee, $caller, $minitrace) = self::getCalleeInfo(
275
            debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS),
276
            $num_args
277
        );
278
279
        // set mode for current run
280 View Code Duplication
        if (self::$enabled_mode === true) {
281
            self::$enabled_mode = self::$mode_default;
282
            if (PHP_SAPI === 'cli' && self::$cli_detection === true) {
283
                self::$enabled_mode = self::$mode_default_cli;
284
            }
285
        }
286
287
        if (in_array('~', $modifiers)) {
288
            self::$enabled_mode = self::MODE_TEXT;
289
        }
290
291
        if (!array_key_exists(self::$enabled_mode, self::$renderers)) {
292
            $renderer = self::$renderers[self::MODE_PLAIN];
293
        } else {
294
            $renderer = self::$renderers[self::$enabled_mode];
295
        }
296
297
        // process modifiers: @, +, ! and -
298
        if (in_array('-', $modifiers)) {
299
            while (ob_get_level()) {
300
                ob_end_clean();
301
            }
302
        }
303
        if (in_array('!', $modifiers)) {
304
            self::$expanded = true;
305
        }
306
        if (in_array('+', $modifiers)) {
307
            self::$max_depth = 0;
308
        }
309
        if (in_array('@', $modifiers)) {
310
            self::$return = true;
311
        }
312
313
        $renderer = new $renderer(array(
314
            'num_args' => $num_args,
315
            'params' => $params,
316
            'modifiers' => $modifiers,
317
            'callee' => $callee,
318
            'caller' => $caller,
319
            'minitrace' => $minitrace,
320
            'settings' => self::settings(),
321
            'stash' => $stash,
322
        ));
323
324
        $plugins = array();
325
326
        foreach (self::$plugins as $plugin) {
327
            if ($plugin instanceof Plugin) {
328
                $plugins[] = $plugin;
329
            } elseif (is_string($plugin) && is_subclass_of($plugin, 'Kint\\Parser\\Plugin')) {
330
                if (!isset(self::$plugin_pool[$plugin])) {
331
                    $p = new $plugin();
332
                    self::$plugin_pool[$plugin] = $p;
333
                }
334
                $plugins[] = self::$plugin_pool[$plugin];
335
            }
336
        }
337
338
        $plugins = $renderer->parserPlugins($plugins);
339
340
        $output = $renderer->preRender();
341
342
        $parser = new Parser(self::$max_depth, empty($caller['class']) ? null : $caller['class']);
343
344
        foreach ($plugins as $plugin) {
345
            $parser->addPlugin($plugin);
346
        }
347
348
        // Kint::dump(1) shorthand
349
        if (!self::$dump_array && (!isset($params[0]['name']) || $params[0]['name'] == '1') && $num_args === 1 && $data === 1) {
350
            $data = debug_backtrace(true);
0 ignored issues
show
Consider using a different name than the parameter $data. This often makes code more readable.
Loading history...
351
            $trace = array();
352
353
            // No need to normalize as we've already called it through getCalleeInfo at this point
354 View Code Duplication
            foreach ($data as $index => $frame) {
355
                if (TracePlugin::frameIsListed($frame, self::$aliases)) {
356
                    $trace = array();
357
                }
358
359
                $trace[] = $frame;
360
            }
361
362
            $lastframe = reset($trace);
363
            $tracename = $lastframe['function'].'(1)';
364
            if (isset($lastframe['class'], $lastframe['type'])) {
365
                $tracename = $lastframe['class'].$lastframe['type'].$tracename;
366
            }
367
            $tracebase = BasicObject::blank($tracename, 'debug_backtrace()');
368
369
            $output .= $renderer->render($parser->parse($trace, $tracebase));
370
        } else {
371
            $data = func_get_args();
0 ignored issues
show
Consider using a different name than the parameter $data. This often makes code more readable.
Loading history...
372
            if ($data === array()) {
373
                $output .= $renderer->render(new NothingObject());
374
            }
375
376
            if (self::$dump_array) {
377
                $data = $data[0];
0 ignored issues
show
Consider using a different name than the parameter $data. This often makes code more readable.
Loading history...
378
            }
379
380
            static $blacklist = array('null', 'true', 'false', 'array(...)', 'array()', '"..."', 'b"..."', '[...]', '[]', '(...)', '()');
381
382
            foreach ($data as $i => $argument) {
383
                if (isset(self::$names[$i])) {
384
                    $output .= $renderer->render(
385
                        $parser->parse($argument, self::$names[$i])
386
                    );
387
                    continue;
388
                }
389
390
                if (!isset($params[$i]['name']) || is_numeric($params[$i]['name']) || in_array(str_replace("'", '"', strtolower($params[$i]['name'])), $blacklist, true)) {
391
                    $name = null;
392
                } else {
393
                    $name = $params[$i]['name'];
394
                }
395
396
                if (isset($params[$i]['path'])) {
397
                    $access_path = $params[$i]['path'];
398
399
                    if (!empty($params[$i]['expression'])) {
400
                        $access_path = '('.$access_path.')';
401
                    }
402
                } else {
403
                    $access_path = '$'.$i;
404
                }
405
406
                $output .= $renderer->render(
407
                    $parser->parse($argument, BasicObject::blank($name, $access_path))
408
                );
409
            }
410
        }
411
412
        $output .= $renderer->postRender();
413
414
        if (self::$return) {
415
            self::settings($stash);
416
417
            return $output;
418
        }
419
420
        self::settings($stash);
421
        echo $output;
422
423
        return 0;
424
    }
425
426
    /**
427
     * generic path display callback, can be configured in app_root_dirs; purpose is
428
     * to show relevant path info and hide as much of the path as possible.
429
     *
430
     * @param string $file
431
     *
432
     * @return string
433
     */
434
    public static function shortenPath($file)
435
    {
436
        $file = array_values(array_filter(explode('/', str_replace('\\', '/', $file)), 'strlen'));
0 ignored issues
show
Consider using a different name than the parameter $file. This often makes code more readable.
Loading history...
437
438
        $longest_match = 0;
439
        $match = '/';
440
441
        foreach (self::$app_root_dirs as $path => $alias) {
442
            if (empty($path)) {
443
                continue;
444
            }
445
446
            $path = array_values(array_filter(explode('/', str_replace('\\', '/', $path)), 'strlen'));
447
448
            if (array_slice($file, 0, count($path)) === $path && count($path) > $longest_match) {
449
                $longest_match = count($path);
450
                $match = $alias;
451
            }
452
        }
453
454
        if ($longest_match) {
455
            $file = array_merge(array($match), array_slice($file, $longest_match));
0 ignored issues
show
Consider using a different name than the parameter $file. This often makes code more readable.
Loading history...
456
457
            return implode('/', $file);
458
        } else {
459
            // fallback to find common path with Kint dir
460
            $kint = array_values(array_filter(explode('/', str_replace('\\', '/', KINT_DIR)), 'strlen'));
461
462
            foreach ($file as $i => $part) {
463
                if (!isset($kint[$i]) || $kint[$i] !== $part) {
464
                    return ($i ? '.../' : '/').implode('/', array_slice($file, $i));
465
                }
466
            }
467
468
            return '/'.implode('/', $file);
469
        }
470
    }
471
472
    public static function getIdeLink($file, $line)
473
    {
474
        return str_replace(array('%f', '%l'), array($file, $line), self::$file_link_format);
475
    }
476
477
    /**
478
     * returns parameter names that the function was passed, as well as any predefined symbols before function
479
     * call (modifiers).
480
     *
481
     * @param array $trace
482
     *
483
     * @return array($params, $modifiers, $callee, $caller, $miniTrace)
484
     */
485
    private static function getCalleeInfo($trace, $num_params)
0 ignored issues
show
Coding Style Naming introduced by
The parameter $num_params is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
486
    {
487
        TracePlugin::normalizeAliases(self::$aliases);
488
        $miniTrace = array();
489
490 View Code Duplication
        foreach ($trace as $index => $frame) {
491
            if (TracePlugin::frameIsListed($frame, self::$aliases)) {
492
                $miniTrace = array();
493
            }
494
495
            if (!TracePlugin::frameIsListed($frame, array('spl_autoload_call'))) {
496
                $miniTrace[] = $frame;
497
            }
498
        }
499
500
        $callee = reset($miniTrace);
501
        $caller = next($miniTrace);
502
        if (!$callee) {
503
            $callee = null;
504
        }
505
        if (!$caller) {
506
            $caller = null;
507
        }
508
509
        unset($miniTrace[0]);
510
511
        foreach ($miniTrace as $index => &$frame) {
512
            if (!isset($frame['file'], $frame['line'])) {
513
                unset($miniTrace[$index]);
514
            } else {
515
                unset($frame['object'], $frame['args']);
516
            }
517
        }
518
519
        $miniTrace = array_values($miniTrace);
520
521
        if (!isset($callee['file'], $callee['line']) || !is_readable($callee['file'])) {
522
            return array(null, array(), $callee, $caller, $miniTrace);
523
        }
524
525
        // open the file and read it up to the position where the function call expression ended
526
        if (empty($callee['class'])) {
527
            $callfunc = $callee['function'];
528
        } else {
529
            $callfunc = array($callee['class'], $callee['function']);
530
        }
531
532
        $calls = CallFinder::getFunctionCalls(
533
            file_get_contents($callee['file']),
534
            $callee['line'],
535
            $callfunc
536
        );
537
538
        $return = array(null, array(), $callee, $caller, $miniTrace);
539
540
        foreach ($calls as $call) {
541
            $is_unpack = false;
542
543
            // Handle argument unpacking as a last resort
544
            if (KINT_PHP56) {
545
                foreach ($call['parameters'] as $i => &$param) {
546
                    if (strpos($param['name'], '...') === 0) {
547
                        if ($i === count($call['parameters']) - 1) {
548
                            for ($j = 1; $j + $i < $num_params; ++$j) {
549
                                $call['parameters'][] = array(
550
                                    'name' => 'array_values('.substr($param['name'], 3).')['.$j.']',
551
                                    'path' => 'array_values('.substr($param['path'], 3).')['.$j.']',
552
                                    'expression' => false,
553
                                );
554
                            }
555
556
                            $param['name'] = 'reset('.substr($param['name'], 3).')';
557
                            $param['path'] = 'reset('.substr($param['path'], 3).')';
558
                            $param['expression'] = false;
559
                        } else {
560
                            $call['parameters'] = array_slice($call['parameters'], 0, $i);
561
                        }
562
563
                        $is_unpack = true;
564
                        break;
565
                    }
566
                }
567
            }
568
569
            if ($is_unpack || count($call['parameters']) === $num_params) {
570
                if ($return[0] === null) {
571
                    $return = array($call['parameters'], $call['modifiers'], $callee, $caller, $miniTrace);
572
                } else {
573
                    // If we have multiple calls on the same line with the same amount of arguments,
574
                    // we can't be sure which it is so just return null and let them figure it out
575
                    return array(null, array(), $callee, $caller, $miniTrace);
576
                }
577
            }
578
        }
579
580
        return $return;
581
    }
582
583
    public static function composerGetExtras($key = 'kint')
584
    {
585
        $extras = array();
586
587
        $folder = KINT_DIR.'/vendor';
588
589
        for ($i = 0; $i < 4; ++$i) {
590
            $installed = $folder.'/composer/installed.json';
591
592
            if (file_exists($installed) && is_readable($installed)) {
593
                $packages = json_decode(file_get_contents($installed), true);
594
595
                foreach ($packages as $package) {
596 View Code Duplication
                    if (isset($package['extra'][$key]) && is_array($package['extra'][$key])) {
597
                        $extras = array_replace($extras, $package['extra'][$key]);
598
                    }
599
                }
600
601
                $folder = dirname($folder);
602
603
                if (file_exists($folder.'/composer.json') && is_readable($folder.'/composer.json')) {
604
                    $composer = json_decode(file_get_contents($folder.'/composer.json'), true);
605
606 View Code Duplication
                    if (isset($composer['extra'][$key]) && is_array($composer['extra'][$key])) {
607
                        $extras = array_replace($extras, $composer['extra'][$key]);
608
                    }
609
                }
610
611
                break;
612
            } else {
613
                $folder = dirname($folder);
614
            }
615
        }
616
617
        return $extras;
618
    }
619
620
    public static function composerGetDisableHelperFunctions()
621
    {
622
        $extras = self::composerGetExtras();
623
624
        return !empty($extras['disable-helper-functions']);
625
    }
626
}
627