1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/* |
6
|
|
|
* The MIT License (MIT) |
7
|
|
|
* |
8
|
|
|
* Copyright (c) 2013 Jonathan Vollebregt ([email protected]), Rokas Šleinius ([email protected]) |
9
|
|
|
* |
10
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of |
11
|
|
|
* this software and associated documentation files (the "Software"), to deal in |
12
|
|
|
* the Software without restriction, including without limitation the rights to |
13
|
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of |
14
|
|
|
* the Software, and to permit persons to whom the Software is furnished to do so, |
15
|
|
|
* subject to the following conditions: |
16
|
|
|
* |
17
|
|
|
* The above copyright notice and this permission notice shall be included in all |
18
|
|
|
* copies or substantial portions of the Software. |
19
|
|
|
* |
20
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
21
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS |
22
|
|
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
23
|
|
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER |
24
|
|
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
25
|
|
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
26
|
|
|
*/ |
27
|
|
|
|
28
|
|
|
namespace Kint; |
29
|
|
|
|
30
|
|
|
use InvalidArgumentException; |
31
|
|
|
use Kint\Parser\ConstructablePluginInterface; |
32
|
|
|
use Kint\Parser\Parser; |
33
|
|
|
use Kint\Parser\PluginInterface; |
34
|
|
|
use Kint\Renderer\ConstructableRendererInterface; |
35
|
|
|
use Kint\Renderer\RendererInterface; |
36
|
|
|
use Kint\Renderer\TextRenderer; |
37
|
|
|
use Kint\Value\Context\BaseContext; |
38
|
|
|
use Kint\Value\Context\ContextInterface; |
39
|
|
|
use Kint\Value\UninitializedValue; |
40
|
|
|
|
41
|
|
|
/** |
42
|
|
|
* @psalm-consistent-constructor |
43
|
|
|
* Psalm bug #8523 |
44
|
|
|
* |
45
|
|
|
* @psalm-import-type CallParameter from CallFinder |
46
|
|
|
* |
47
|
|
|
* @psalm-type KintMode = Kint::MODE_*|bool |
48
|
|
|
* |
49
|
|
|
* @psalm-api |
50
|
|
|
*/ |
51
|
|
|
class Kint implements FacadeInterface |
52
|
|
|
{ |
53
|
|
|
public const MODE_RICH = 'r'; |
54
|
|
|
public const MODE_TEXT = 't'; |
55
|
|
|
public const MODE_CLI = 'c'; |
56
|
|
|
public const MODE_PLAIN = 'p'; |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* @var mixed Kint mode |
60
|
|
|
* |
61
|
|
|
* false: Disabled |
62
|
|
|
* true: Enabled, default mode selection |
63
|
|
|
* other: Manual mode selection |
64
|
|
|
* |
65
|
|
|
* @psalm-var KintMode |
66
|
|
|
*/ |
67
|
|
|
public static $enabled_mode = true; |
68
|
|
|
|
69
|
|
|
/** |
70
|
|
|
* Default mode. |
71
|
|
|
* |
72
|
|
|
* @psalm-var KintMode |
73
|
|
|
*/ |
74
|
|
|
public static $mode_default = self::MODE_RICH; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Default mode in CLI with cli_detection on. |
78
|
|
|
* |
79
|
|
|
* @psalm-var KintMode |
80
|
|
|
*/ |
81
|
|
|
public static $mode_default_cli = self::MODE_CLI; |
82
|
|
|
|
83
|
|
|
/** |
84
|
|
|
* @var bool enable detection when Kint is command line. |
85
|
|
|
* |
86
|
|
|
* Formats output with whitespace only; does not HTML-escape it |
87
|
|
|
*/ |
88
|
|
|
public static bool $cli_detection = true; |
89
|
|
|
|
90
|
|
|
/** |
91
|
|
|
* @var bool Return output instead of echoing |
92
|
|
|
*/ |
93
|
|
|
public static bool $return = false; |
94
|
|
|
|
95
|
|
|
/** |
96
|
|
|
* @var int depth limit for array/object traversal. 0 for no limit |
97
|
|
|
*/ |
98
|
|
|
public static int $depth_limit = 7; |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* @var bool expand all trees by default for rich view |
102
|
|
|
*/ |
103
|
|
|
public static bool $expanded = false; |
104
|
|
|
|
105
|
|
|
/** |
106
|
|
|
* @var bool whether to display where kint was called from |
107
|
|
|
*/ |
108
|
|
|
public static bool $display_called_from = true; |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* @var array Kint aliases. Add debug functions in Kint wrappers here to fix modifiers and backtraces |
112
|
|
|
*/ |
113
|
|
|
public static array $aliases = [ |
114
|
|
|
[self::class, 'dump'], |
115
|
|
|
[self::class, 'trace'], |
116
|
|
|
[self::class, 'dumpAll'], |
117
|
|
|
]; |
118
|
|
|
|
119
|
|
|
/** |
120
|
|
|
* @psalm-var array<RendererInterface|class-string<ConstructableRendererInterface>> |
121
|
|
|
* |
122
|
|
|
* Array of modes to renderer class names |
123
|
|
|
*/ |
124
|
|
|
public static array $renderers = [ |
125
|
|
|
self::MODE_RICH => Renderer\RichRenderer::class, |
126
|
|
|
self::MODE_PLAIN => Renderer\PlainRenderer::class, |
127
|
|
|
self::MODE_TEXT => TextRenderer::class, |
128
|
|
|
self::MODE_CLI => Renderer\CliRenderer::class, |
129
|
|
|
]; |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* @psalm-var array<PluginInterface|class-string<ConstructablePluginInterface>> |
133
|
|
|
*/ |
134
|
|
|
public static array $plugins = [ |
135
|
|
|
\Kint\Parser\ArrayLimitPlugin::class, |
136
|
|
|
\Kint\Parser\ArrayObjectPlugin::class, |
137
|
|
|
\Kint\Parser\Base64Plugin::class, |
138
|
|
|
\Kint\Parser\BinaryPlugin::class, |
139
|
|
|
\Kint\Parser\BlacklistPlugin::class, |
140
|
|
|
\Kint\Parser\ClassHooksPlugin::class, |
141
|
|
|
\Kint\Parser\ClassMethodsPlugin::class, |
142
|
|
|
\Kint\Parser\ClassStaticsPlugin::class, |
143
|
|
|
\Kint\Parser\ClassStringsPlugin::class, |
144
|
|
|
\Kint\Parser\ClosurePlugin::class, |
145
|
|
|
\Kint\Parser\ColorPlugin::class, |
146
|
|
|
\Kint\Parser\DateTimePlugin::class, |
147
|
|
|
\Kint\Parser\DomPlugin::class, |
148
|
|
|
\Kint\Parser\EnumPlugin::class, |
149
|
|
|
\Kint\Parser\FsPathPlugin::class, |
150
|
|
|
\Kint\Parser\HtmlPlugin::class, |
151
|
|
|
\Kint\Parser\IteratorPlugin::class, |
152
|
|
|
\Kint\Parser\JsonPlugin::class, |
153
|
|
|
\Kint\Parser\MicrotimePlugin::class, |
154
|
|
|
\Kint\Parser\MysqliPlugin::class, |
155
|
|
|
// \Kint\Parser\SerializePlugin::class, |
156
|
|
|
\Kint\Parser\SimpleXMLElementPlugin::class, |
157
|
|
|
\Kint\Parser\SplFileInfoPlugin::class, |
158
|
|
|
\Kint\Parser\StreamPlugin::class, |
159
|
|
|
\Kint\Parser\TablePlugin::class, |
160
|
|
|
\Kint\Parser\ThrowablePlugin::class, |
161
|
|
|
\Kint\Parser\TimestampPlugin::class, |
162
|
|
|
\Kint\Parser\ToStringPlugin::class, |
163
|
|
|
\Kint\Parser\TracePlugin::class, |
164
|
|
|
\Kint\Parser\XmlPlugin::class, |
165
|
|
|
]; |
166
|
|
|
|
167
|
|
|
protected Parser $parser; |
168
|
|
|
protected RendererInterface $renderer; |
169
|
|
|
|
170
|
|
|
public function __construct(Parser $p, RendererInterface $r) |
171
|
|
|
{ |
172
|
|
|
$this->parser = $p; |
173
|
|
|
$this->renderer = $r; |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
public function setParser(Parser $p): void |
177
|
|
|
{ |
178
|
|
|
$this->parser = $p; |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
public function getParser(): Parser |
182
|
|
|
{ |
183
|
|
|
return $this->parser; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
public function setRenderer(RendererInterface $r): void |
187
|
|
|
{ |
188
|
|
|
$this->renderer = $r; |
189
|
|
|
} |
190
|
|
|
|
191
|
|
|
public function getRenderer(): RendererInterface |
192
|
|
|
{ |
193
|
|
|
return $this->renderer; |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
public function setStatesFromStatics(array $statics): void |
197
|
|
|
{ |
198
|
|
|
$this->renderer->setStatics($statics); |
199
|
|
|
|
200
|
|
|
$this->parser->setDepthLimit($statics['depth_limit'] ?? 0); |
201
|
|
|
$this->parser->clearPlugins(); |
202
|
|
|
|
203
|
|
|
if (!isset($statics['plugins'])) { |
204
|
|
|
return; |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
$plugins = []; |
208
|
|
|
|
209
|
|
|
foreach ($statics['plugins'] as $plugin) { |
210
|
|
|
if ($plugin instanceof PluginInterface) { |
211
|
|
|
$plugins[] = $plugin; |
212
|
|
|
} elseif (\is_string($plugin) && \is_a($plugin, ConstructablePluginInterface::class, true)) { |
213
|
|
|
$plugins[] = new $plugin($this->parser); |
214
|
|
|
} |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
$plugins = $this->renderer->filterParserPlugins($plugins); |
218
|
|
|
|
219
|
|
|
foreach ($plugins as $plugin) { |
220
|
|
|
try { |
221
|
|
|
$this->parser->addPlugin($plugin); |
222
|
|
|
} catch (InvalidArgumentException $e) { |
223
|
|
|
\trigger_error( |
224
|
|
|
'Plugin '.Utils::errorSanitizeString(\get_class($plugin)).' could not be added to a Kint parser: '.Utils::errorSanitizeString($e->getMessage()), |
225
|
|
|
E_USER_WARNING |
226
|
|
|
); |
227
|
|
|
} |
228
|
|
|
} |
229
|
|
|
} |
230
|
|
|
|
231
|
|
|
public function setStatesFromCallInfo(array $info): void |
232
|
|
|
{ |
233
|
|
|
$this->renderer->setCallInfo($info); |
234
|
|
|
|
235
|
|
|
if (isset($info['modifiers']) && \is_array($info['modifiers']) && \in_array('+', $info['modifiers'], true)) { |
236
|
|
|
$this->parser->setDepthLimit(0); |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
$this->parser->setCallerClass($info['caller']['class'] ?? null); |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
public function dumpAll(array $vars, array $base): string |
243
|
|
|
{ |
244
|
|
|
if (\array_keys($vars) !== \array_keys($base)) { |
245
|
|
|
throw new InvalidArgumentException('Kint::dumpAll requires arrays of identical size and keys as arguments'); |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
if ([] === $vars) { |
249
|
|
|
return $this->dumpNothing(); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
$output = $this->renderer->preRender(); |
253
|
|
|
|
254
|
|
|
foreach ($vars as $key => $_) { |
255
|
|
|
if (!$base[$key] instanceof ContextInterface) { |
256
|
|
|
throw new InvalidArgumentException('Kint::dumpAll requires all elements of the second argument to be ContextInterface instances'); |
257
|
|
|
} |
258
|
|
|
$output .= $this->dumpVar($vars[$key], $base[$key]); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
$output .= $this->renderer->postRender(); |
262
|
|
|
|
263
|
|
|
return $output; |
264
|
|
|
} |
265
|
|
|
|
266
|
|
|
protected function dumpNothing(): string |
267
|
|
|
{ |
268
|
|
|
$output = $this->renderer->preRender(); |
269
|
|
|
$output .= $this->renderer->render(new UninitializedValue(new BaseContext('No argument'))); |
270
|
|
|
$output .= $this->renderer->postRender(); |
271
|
|
|
|
272
|
|
|
return $output; |
273
|
|
|
} |
274
|
|
|
|
275
|
|
|
/** |
276
|
|
|
* Dumps and renders a var. |
277
|
|
|
* |
278
|
|
|
* @param mixed &$var Data to dump |
279
|
|
|
*/ |
280
|
|
|
protected function dumpVar(&$var, ContextInterface $c): string |
281
|
|
|
{ |
282
|
|
|
return $this->renderer->render( |
283
|
|
|
$this->parser->parse($var, $c) |
284
|
|
|
); |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Gets all static settings at once. |
289
|
|
|
* |
290
|
|
|
* @return array Current static settings |
291
|
|
|
*/ |
292
|
|
|
public static function getStatics(): array |
293
|
|
|
{ |
294
|
|
|
return [ |
295
|
|
|
'aliases' => static::$aliases, |
296
|
|
|
'cli_detection' => static::$cli_detection, |
297
|
|
|
'depth_limit' => static::$depth_limit, |
298
|
|
|
'display_called_from' => static::$display_called_from, |
299
|
|
|
'enabled_mode' => static::$enabled_mode, |
300
|
|
|
'expanded' => static::$expanded, |
301
|
|
|
'mode_default' => static::$mode_default, |
302
|
|
|
'mode_default_cli' => static::$mode_default_cli, |
303
|
|
|
'plugins' => static::$plugins, |
304
|
|
|
'renderers' => static::$renderers, |
305
|
|
|
'return' => static::$return, |
306
|
|
|
]; |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
/** |
310
|
|
|
* Creates a Kint instance based on static settings. |
311
|
|
|
* |
312
|
|
|
* @param array $statics array of statics as returned by getStatics |
313
|
|
|
*/ |
314
|
|
|
public static function createFromStatics(array $statics): ?FacadeInterface |
315
|
|
|
{ |
316
|
|
|
$mode = false; |
317
|
|
|
|
318
|
|
|
if (isset($statics['enabled_mode'])) { |
319
|
|
|
$mode = $statics['enabled_mode']; |
320
|
|
|
|
321
|
|
|
if (true === $mode && isset($statics['mode_default'])) { |
322
|
|
|
$mode = $statics['mode_default']; |
323
|
|
|
|
324
|
|
|
if (PHP_SAPI === 'cli' && !empty($statics['cli_detection']) && isset($statics['mode_default_cli'])) { |
325
|
|
|
$mode = $statics['mode_default_cli']; |
326
|
|
|
} |
327
|
|
|
} |
328
|
|
|
} |
329
|
|
|
|
330
|
|
|
if (false === $mode) { |
331
|
|
|
return null; |
332
|
|
|
} |
333
|
|
|
|
334
|
|
|
$renderer = null; |
335
|
|
|
if (isset($statics['renderers'][$mode])) { |
336
|
|
|
if ($statics['renderers'][$mode] instanceof RendererInterface) { |
337
|
|
|
$renderer = $statics['renderers'][$mode]; |
338
|
|
|
} |
339
|
|
|
|
340
|
|
|
if (\is_a($statics['renderers'][$mode], ConstructableRendererInterface::class, true)) { |
341
|
|
|
$renderer = new $statics['renderers'][$mode](); |
342
|
|
|
} |
343
|
|
|
} |
344
|
|
|
|
345
|
|
|
$renderer ??= new TextRenderer(); |
346
|
|
|
|
347
|
|
|
return new static(new Parser(), $renderer); |
348
|
|
|
} |
349
|
|
|
|
350
|
|
|
/** |
351
|
|
|
* Creates base contexts given parameter info. |
352
|
|
|
* |
353
|
|
|
* @psalm-param list<CallParameter> $params |
354
|
|
|
* |
355
|
|
|
* @return BaseContext[] Base contexts for the arguments |
356
|
|
|
*/ |
357
|
|
|
public static function getBasesFromParamInfo(array $params, int $argc): array |
358
|
|
|
{ |
359
|
|
|
$bases = []; |
360
|
|
|
|
361
|
|
|
for ($i = 0; $i < $argc; ++$i) { |
362
|
|
|
$param = $params[$i] ?? null; |
363
|
|
|
|
364
|
|
|
if (!empty($param['literal'])) { |
365
|
|
|
$name = 'literal'; |
366
|
|
|
} else { |
367
|
|
|
$name = $param['name'] ?? '$'.$i; |
368
|
|
|
} |
369
|
|
|
|
370
|
|
|
if (isset($param['path'])) { |
371
|
|
|
$access_path = $param['path']; |
372
|
|
|
|
373
|
|
|
if ($param['expression']) { |
374
|
|
|
$access_path = '('.$access_path.')'; |
375
|
|
|
} elseif ($param['new_without_parens']) { |
376
|
|
|
$access_path .= '()'; |
377
|
|
|
} |
378
|
|
|
} else { |
379
|
|
|
$access_path = '$'.$i; |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
$base = new BaseContext($name); |
383
|
|
|
$base->access_path = $access_path; |
384
|
|
|
$bases[] = $base; |
385
|
|
|
} |
386
|
|
|
|
387
|
|
|
return $bases; |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
/** |
391
|
|
|
* Gets call info from the backtrace, alias, and argument count. |
392
|
|
|
* |
393
|
|
|
* Aliases must be normalized beforehand (Utils::normalizeAliases) |
394
|
|
|
* |
395
|
|
|
* @param array $aliases Call aliases as found in Kint::$aliases |
396
|
|
|
* @param array[] $trace Backtrace |
397
|
|
|
* @param array $args Arguments |
398
|
|
|
* |
399
|
|
|
* @return array Call info |
400
|
|
|
* |
401
|
|
|
* @psalm-param list<non-empty-array> $trace |
402
|
|
|
*/ |
403
|
|
|
public static function getCallInfo(array $aliases, array $trace, array $args): array |
404
|
|
|
{ |
405
|
|
|
$found = false; |
406
|
|
|
$callee = null; |
407
|
|
|
$caller = null; |
408
|
|
|
$miniTrace = []; |
409
|
|
|
|
410
|
|
|
foreach ($trace as $frame) { |
411
|
|
|
if (Utils::traceFrameIsListed($frame, $aliases)) { |
412
|
|
|
$found = true; |
413
|
|
|
$miniTrace = []; |
414
|
|
|
} |
415
|
|
|
|
416
|
|
|
if (!Utils::traceFrameIsListed($frame, ['spl_autoload_call'])) { |
417
|
|
|
$miniTrace[] = $frame; |
418
|
|
|
} |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
if ($found) { |
422
|
|
|
$callee = \reset($miniTrace) ?: null; |
423
|
|
|
$caller = \next($miniTrace) ?: null; |
424
|
|
|
} |
425
|
|
|
|
426
|
|
|
foreach ($miniTrace as $index => $frame) { |
427
|
|
|
if ((0 === $index && $callee === $frame) || isset($frame['file'], $frame['line'])) { |
428
|
|
|
unset($frame['object'], $frame['args']); |
429
|
|
|
$miniTrace[$index] = $frame; |
430
|
|
|
} else { |
431
|
|
|
unset($miniTrace[$index]); |
432
|
|
|
} |
433
|
|
|
} |
434
|
|
|
|
435
|
|
|
$miniTrace = \array_values($miniTrace); |
436
|
|
|
|
437
|
|
|
$call = static::getSingleCall($callee ?: [], $args); |
438
|
|
|
|
439
|
|
|
$ret = [ |
440
|
|
|
'params' => null, |
441
|
|
|
'modifiers' => [], |
442
|
|
|
'callee' => $callee, |
443
|
|
|
'caller' => $caller, |
444
|
|
|
'trace' => $miniTrace, |
445
|
|
|
]; |
446
|
|
|
|
447
|
|
|
if (null !== $call) { |
448
|
|
|
$ret['params'] = $call['parameters']; |
449
|
|
|
$ret['modifiers'] = $call['modifiers']; |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
return $ret; |
453
|
|
|
} |
454
|
|
|
|
455
|
|
|
/** |
456
|
|
|
* Dumps a backtrace. |
457
|
|
|
* |
458
|
|
|
* Functionally equivalent to Kint::dump(1) or Kint::dump(debug_backtrace(true)) |
459
|
|
|
* |
460
|
|
|
* @return int|string |
461
|
|
|
*/ |
462
|
|
|
public static function trace() |
463
|
|
|
{ |
464
|
|
|
if (false === static::$enabled_mode) { |
465
|
|
|
return 0; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
static::$aliases = Utils::normalizeAliases(static::$aliases); |
469
|
|
|
|
470
|
|
|
$call_info = static::getCallInfo(static::$aliases, \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), []); |
471
|
|
|
|
472
|
|
|
$statics = static::getStatics(); |
473
|
|
|
|
474
|
|
|
if (\in_array('~', $call_info['modifiers'], true)) { |
475
|
|
|
$statics['enabled_mode'] = static::MODE_TEXT; |
476
|
|
|
} |
477
|
|
|
|
478
|
|
|
$kintstance = static::createFromStatics($statics); |
479
|
|
|
if (!$kintstance) { |
|
|
|
|
480
|
|
|
return 0; |
481
|
|
|
} |
482
|
|
|
|
483
|
|
|
if (\in_array('-', $call_info['modifiers'], true)) { |
484
|
|
|
while (\ob_get_level()) { |
485
|
|
|
\ob_end_clean(); |
486
|
|
|
} |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
$kintstance->setStatesFromStatics($statics); |
490
|
|
|
$kintstance->setStatesFromCallInfo($call_info); |
491
|
|
|
|
492
|
|
|
$trimmed_trace = []; |
493
|
|
|
$trace = \debug_backtrace(); |
494
|
|
|
|
495
|
|
|
foreach ($trace as $frame) { |
496
|
|
|
if (Utils::traceFrameIsListed($frame, static::$aliases)) { |
497
|
|
|
$trimmed_trace = []; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
$trimmed_trace[] = $frame; |
501
|
|
|
} |
502
|
|
|
|
503
|
|
|
\array_shift($trimmed_trace); |
504
|
|
|
|
505
|
|
|
$base = new BaseContext('Kint\\Kint::trace()'); |
506
|
|
|
$base->access_path = 'debug_backtrace()'; |
507
|
|
|
$output = $kintstance->dumpAll([$trimmed_trace], [$base]); |
508
|
|
|
|
509
|
|
|
if (static::$return || \in_array('@', $call_info['modifiers'], true)) { |
510
|
|
|
return $output; |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
echo $output; |
514
|
|
|
|
515
|
|
|
if (\in_array('-', $call_info['modifiers'], true)) { |
516
|
|
|
\flush(); // @codeCoverageIgnore |
517
|
|
|
} |
518
|
|
|
|
519
|
|
|
return 0; |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
/** |
523
|
|
|
* Dumps some data. |
524
|
|
|
* |
525
|
|
|
* Functionally equivalent to Kint::dump(1) or Kint::dump(debug_backtrace()) |
526
|
|
|
* |
527
|
|
|
* @psalm-param mixed ...$args |
528
|
|
|
* |
529
|
|
|
* @return int|string |
530
|
|
|
*/ |
531
|
|
|
public static function dump(...$args) |
532
|
|
|
{ |
533
|
|
|
if (false === static::$enabled_mode) { |
534
|
|
|
return 0; |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
static::$aliases = Utils::normalizeAliases(static::$aliases); |
538
|
|
|
|
539
|
|
|
$call_info = static::getCallInfo(static::$aliases, \debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), $args); |
540
|
|
|
|
541
|
|
|
$statics = static::getStatics(); |
542
|
|
|
|
543
|
|
|
if (\in_array('~', $call_info['modifiers'], true)) { |
544
|
|
|
$statics['enabled_mode'] = static::MODE_TEXT; |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
$kintstance = static::createFromStatics($statics); |
548
|
|
|
if (!$kintstance) { |
|
|
|
|
549
|
|
|
return 0; |
550
|
|
|
} |
551
|
|
|
|
552
|
|
|
if (\in_array('-', $call_info['modifiers'], true)) { |
553
|
|
|
while (\ob_get_level()) { |
554
|
|
|
\ob_end_clean(); |
555
|
|
|
} |
556
|
|
|
} |
557
|
|
|
|
558
|
|
|
$kintstance->setStatesFromStatics($statics); |
559
|
|
|
$kintstance->setStatesFromCallInfo($call_info); |
560
|
|
|
|
561
|
|
|
$bases = static::getBasesFromParamInfo($call_info['params'] ?? [], \count($args)); |
562
|
|
|
$output = $kintstance->dumpAll(\array_values($args), $bases); |
563
|
|
|
|
564
|
|
|
if (static::$return || \in_array('@', $call_info['modifiers'], true)) { |
565
|
|
|
return $output; |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
echo $output; |
569
|
|
|
|
570
|
|
|
if (\in_array('-', $call_info['modifiers'], true)) { |
571
|
|
|
\flush(); // @codeCoverageIgnore |
572
|
|
|
} |
573
|
|
|
|
574
|
|
|
return 0; |
575
|
|
|
} |
576
|
|
|
|
577
|
|
|
/** |
578
|
|
|
* Returns specific function call info from a stack trace frame, or null if no match could be found. |
579
|
|
|
* |
580
|
|
|
* @param array $frame The stack trace frame in question |
581
|
|
|
* @param array $args The arguments |
582
|
|
|
* |
583
|
|
|
* @return ?array params and modifiers, or null if a specific call could not be determined |
584
|
|
|
*/ |
585
|
|
|
protected static function getSingleCall(array $frame, array $args): ?array |
586
|
|
|
{ |
587
|
|
|
if ( |
588
|
|
|
!isset($frame['file'], $frame['line'], $frame['function']) || |
589
|
|
|
!\is_readable($frame['file']) || |
590
|
|
|
false === ($source = \file_get_contents($frame['file'])) |
591
|
|
|
) { |
592
|
|
|
return null; |
593
|
|
|
} |
594
|
|
|
|
595
|
|
|
if (empty($frame['class'])) { |
596
|
|
|
$callfunc = $frame['function']; |
597
|
|
|
} else { |
598
|
|
|
$callfunc = [$frame['class'], $frame['function']]; |
599
|
|
|
} |
600
|
|
|
|
601
|
|
|
$calls = CallFinder::getFunctionCalls($source, $frame['line'], $callfunc); |
602
|
|
|
|
603
|
|
|
$argc = \count($args); |
604
|
|
|
|
605
|
|
|
$return = null; |
606
|
|
|
|
607
|
|
|
foreach ($calls as $call) { |
608
|
|
|
$is_unpack = false; |
609
|
|
|
|
610
|
|
|
// Handle argument unpacking as a last resort |
611
|
|
|
foreach ($call['parameters'] as $i => &$param) { |
612
|
|
|
if (0 === \strpos($param['name'], '...')) { |
613
|
|
|
$is_unpack = true; |
614
|
|
|
|
615
|
|
|
// If we're on the last param |
616
|
|
|
if ($i < $argc && $i === \count($call['parameters']) - 1) { |
617
|
|
|
unset($call['parameters'][$i]); |
618
|
|
|
|
619
|
|
|
if (Utils::isAssoc($args)) { |
620
|
|
|
// Associated unpacked arrays can be accessed by key |
621
|
|
|
$keys = \array_slice(\array_keys($args), $i); |
622
|
|
|
|
623
|
|
|
foreach ($keys as $key) { |
624
|
|
|
$call['parameters'][] = [ |
625
|
|
|
'name' => \substr($param['name'], 3).'['.\var_export($key, true).']', |
626
|
|
|
'path' => \substr($param['path'], 3).'['.\var_export($key, true).']', |
627
|
|
|
'expression' => false, |
628
|
|
|
'literal' => false, |
629
|
|
|
'new_without_parens' => false, |
630
|
|
|
]; |
631
|
|
|
} |
632
|
|
|
} else { |
633
|
|
|
// Numeric unpacked arrays have their order blown away like a pass |
634
|
|
|
// through array_values so we can't access them directly at all |
635
|
|
|
for ($j = 0; $j + $i < $argc; ++$j) { |
636
|
|
|
$call['parameters'][] = [ |
637
|
|
|
'name' => 'array_values('.\substr($param['name'], 3).')['.$j.']', |
638
|
|
|
'path' => 'array_values('.\substr($param['path'], 3).')['.$j.']', |
639
|
|
|
'expression' => false, |
640
|
|
|
'literal' => false, |
641
|
|
|
'new_without_parens' => false, |
642
|
|
|
]; |
643
|
|
|
} |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
$call['parameters'] = \array_values($call['parameters']); |
647
|
|
|
} else { |
648
|
|
|
$call['parameters'] = \array_slice($call['parameters'], 0, $i); |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
break; |
652
|
|
|
} |
653
|
|
|
|
654
|
|
|
if ($i >= $argc) { |
655
|
|
|
continue 2; |
656
|
|
|
} |
657
|
|
|
} |
658
|
|
|
|
659
|
|
|
if ($is_unpack || \count($call['parameters']) === $argc) { |
660
|
|
|
if (null === $return) { |
661
|
|
|
$return = $call; |
662
|
|
|
} else { |
663
|
|
|
// If we have multiple calls on the same line with the same amount of arguments, |
664
|
|
|
// we can't be sure which it is so just return null and let them figure it out |
665
|
|
|
return null; |
666
|
|
|
} |
667
|
|
|
} |
668
|
|
|
} |
669
|
|
|
|
670
|
|
|
return $return; |
671
|
|
|
} |
672
|
|
|
} |
673
|
|
|
|