Completed
Push — master ( 47f526...6c6127 )
by Lars
02:53
created

Kint   D

Complexity

Total Complexity 138

Size/Duplication

Total Lines 839
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 28.97%

Importance

Changes 7
Bugs 2 Features 3
Metric Value
wmc 138
c 7
b 2
f 3
lcom 2
cbo 2
dl 0
loc 839
ccs 93
cts 321
cp 0.2897
rs 4.4444

11 Methods

Rating   Name   Duplication   Size   Complexity  
A enabled() 0 13 2
A getIdeLink() 0 4 1
A trace() 0 8 3
F _getCalleeInfo() 0 197 36
B _removeAllButCode() 0 36 6
B _showSource() 0 55 7
B _stepIsInternal() 0 14 5
F dump() 0 136 29
B settings() 0 33 4
D shortenPath() 0 37 10
F _parseTrace() 0 125 35

How to fix   Complexity   

Complex Class

Complex classes like Kint often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Kint, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace kint;
4
5
use kint\decorators\Kint_Decorators;
6
use kint\inc\KintParser;
7
8
/**
9
 * Class Kint
10
 *
11
 * @package kint
12
 */
13
class Kint
14
{
15
  // these are all public and 1:1 config array keys so you can switch them easily
16
17
  const MODE_CLI = 'c'; # stores mode and active statuses
18
19
  const MODE_PLAIN = 'p';
20
21
  const MODE_JS = 'j';
22
23
  const MODE_RICH = 'r';
24
25
  const MODE_WHITESPACE = 'w';
26
27
  public static $delayedMode;
28
29
  public static $returnOutput;
30
31
  public static $fileLinkFormat;
32
33
  public static $displayCalledFrom;
34
35
  public static $maxStrLength;
36
37
  public static $appRootDirs;
38
39
  public static $maxLevels;
40
41
  public static $theme;
42
43
  public static $expandedByDefault;
44
45
  public static $cliDetection;
46
47
  public static $cliColors;
48
49
  public static $aliases = array(
50
      'methods'   => array(
51
          array('kint', 'dump'),
52
          array('kint', 'trace'),
53
      ),
54
      'functions' => array(
55
          'd',
56
          'dd',
57
          'ddd',
58
          'de',
59
          's',
60
          'sd',
61
          'se',
62
          'j',
63
          'jd',
64
          'je',
65
      ),
66
  );
67
68
  private static $_enabledMode;
69
70
  /**
71
   * returns parameter names that the function was passed, as well as any predefined symbols before function
72
   * call (modifiers)
73
   *
74
   * @param array $trace
75
   *
76
   * @return array( $parameters, $modifier, $callee, $previousCaller )
0 ignored issues
show
Documentation introduced by
The doc-type array( could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
77
   */
78 1
  private static function _getCalleeInfo($trace)
79
  {
80 1
    $previousCaller = array();
81 1
    $miniTrace = array();
82 1
    $prevStep = array();
83
84
    # go from back of trace to find first occurrence of call to Kint or its wrappers
85
    /** @noinspection PhpAssignmentInConditionInspection */
86 1
    while ($step = array_pop($trace)) {
87
88 1
      if (self::_stepIsInternal($step)) {
89
        $previousCaller = $prevStep;
90
        break;
91 1
      } elseif (isset($step['file'], $step['line'])) {
92 1
        unset($step['object'], $step['args']);
93 1
        array_unshift($miniTrace, $step);
94
      }
95
96 1
      $prevStep = $step;
97
    }
98 1
    $callee = $step;
99
100
    if (
101 1
        !isset($callee['file'])
102
        ||
103 1
        !is_readable($callee['file'])
104
    ) {
105 1
      return array(null, null, null, null, $miniTrace);
106
    }
107
108
    # open the file and read it up to the position where the function call expression ended
109
    $filePointer = fopen($callee['file'], 'r');
110
    $line = 0;
111
    $source = '';
112
    while (($row = fgets($filePointer)) !== false) {
113
      if (++$line > $callee['line']) {
114
        break;
115
      }
116
      $source .= $row;
117
    }
118
    fclose($filePointer);
119
    $source = self::_removeAllButCode($source);
120
121
    if (empty($callee['class'])) {
122
      $codePattern = $callee['function'];
123
    } else {
124
      if ($callee['type'] === '::') {
125
        $codePattern = $callee['class'] . "\x07*" . $callee['type'] . "\x07*" . $callee['function'];
126
      } else {
127
        $codePattern = ".*\x07*" . $callee['type'] . "\x07*" . $callee['function'];
128
      }
129
    }
130
131
    // TODO: if more than one call in one line - not possible to determine variable names
132
    // TODO: does not recognize string concat
133
    # get the position of the last call to the function
134
    preg_match_all(
135
        "
136
            [
137
            # beginning of statement
138
            [\x07{(]
139
140
            # search for modifiers (group 1)
141
            ([-+!@~]*)?
142
143
            # spaces
144
            \x07*
145
146
            # check if output is assigned to a variable (group 2) todo: does not detect concat
147
            (
148
                \\$[a-z0-9_]+ # variable
149
                \x07*\\.?=\x07*  # assignment
150
            )?
151
152
            # possibly a namespace symbol
153
            \\\\?
154
155
            # spaces again
156
            \x07*
157
158
            # main call to Kint
159
            ({$codePattern})
160
161
            # spaces everywhere
162
            \x07*
163
164
            # find the character where kint's opening bracket resides (group 3)
165
            (\\()
166
167
            ]ix",
168
        $source,
169
        $matches,
170
        PREG_OFFSET_CAPTURE
171
    );
172
173
    $modifiers = end($matches[1]);
174
    $assignment = end($matches[2]);
175
    $callToKint = end($matches[3]);
176
    $bracket = end($matches[4]);
177
178
    if (empty($callToKint)) {
179
      # if a wrapper is mis-configured, don't display the whole file as variable name
180
      return array(array(), $modifiers, $callee, $previousCaller, $miniTrace);
181
    }
182
183
    $modifiers = $modifiers[0];
184
    if ($assignment[1] !== -1) {
185
      $modifiers .= '@';
186
    }
187
188
    $paramsString = preg_replace("[\x07+]", ' ', substr($source, $bracket[1] + 1));
189
    # we now have a string like this:
190
    # <parameters passed>); <the rest of the last read line>
191
192
    # remove everything in brackets and quotes, we don't need nested statements nor literal strings which would
193
    # only complicate separating individual arguments
194
    $c = strlen($paramsString);
195
    $inString = $escaped = $openedBracket = $closingBracket = false;
196
    $i = 0;
197
    $inBrackets = 0;
198
    $openedBrackets = array();
199
200
    while ($i < $c) {
201
      $letter = $paramsString[$i];
202
203
      if (!$inString) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $inString of type string|false is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
204
        if ($letter === '\'' || $letter === '"') {
205
          $inString = $letter;
206
        } elseif ($letter === '(' || $letter === '[') {
207
          $inBrackets++;
208
          $openedBrackets[] = $openedBracket = $letter;
209
          $closingBracket = $openedBracket === '(' ? ')' : ']';
210
        } elseif ($inBrackets && $letter === $closingBracket) {
211
          $inBrackets--;
212
          array_pop($openedBrackets);
213
          $openedBracket = end($openedBrackets);
214
          $closingBracket = $openedBracket === '(' ? ')' : ']';
215
        } elseif (!$inBrackets && $letter === ')') {
216
          $paramsString = substr($paramsString, 0, $i);
217
          break;
218
        }
219
      } elseif ($letter === $inString && !$escaped) {
220
        $inString = false;
221
      }
222
223
      # replace whatever was inside quotes or brackets with untypeable characters, we don't
224
      # need that info. We'll later replace the whole string with '...'
225
      if (
226
          $inBrackets > 0
227
          &&
228
          (
229
              $inBrackets > 1
230
              ||
231
              $letter !== $openedBracket
232
          )
233
      ) {
234
        $paramsString[$i] = "\x07";
235
      }
236
237
      if (
238
          $inString
0 ignored issues
show
Bug Best Practice introduced by
The expression $inString of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
239
          &&
240
          (
241
              $letter !== $inString
242
              ||
243
              $escaped
244
          )
245
      ) {
246
        $paramsString[$i] = "\x07";
247
      }
248
249
      $escaped = !$escaped && ($letter === '\\');
250
      $i++;
251
    }
252
253
    # by now we have an un-nested arguments list, lets make it to an array for processing further
254
    $arguments = explode(',', preg_replace("[\x07+]", '...', $paramsString));
255
256
    # test each argument whether it was passed literary or was it an expression or a variable name
257
    $parameters = array();
258
    $blacklist = array('null', 'true', 'false', 'array(...)', 'array()', '"..."', '[...]', 'b"..."',);
259
    foreach ($arguments as $argument) {
260
      $argument = trim($argument);
261
262
      if (
263
          is_numeric($argument)
264
          ||
265
          in_array(str_replace("'", '"', strtolower($argument)), $blacklist, true)
266
      ) {
267
        $parameters[] = null;
268
      } else {
269
        $parameters[] = $argument;
270
      }
271
    }
272
273
    return array($parameters, $modifiers, $callee, $previousCaller, $miniTrace);
274
  }
275
276
  /**
277
   * @param array $data
278
   *
279
   * @return array|false
280
   */
281 1
  private static function _parseTrace(array $data)
282
  {
283 1
    $trace = array();
284 1
    $traceFields = array('file', 'line', 'args', 'class');
285 1
    $fileFound = false; # file element must exist in one of the steps
286
287
    # validate whether a trace was indeed passed
288
    /** @noinspection PhpAssignmentInConditionInspection */
289 1
    while ($step = array_pop($data)) {
290 1
      if (!is_array($step) || !isset($step['function'])) {
291 1
        return false;
292
      }
293
      if (!$fileFound && isset($step['file']) && file_exists($step['file'])) {
294
        $fileFound = true;
295
      }
296
297
      $valid = false;
298
      foreach ($traceFields as $element) {
299
        if (isset($step[$element])) {
300
          $valid = true;
301
          break;
302
        }
303
      }
304
      if (!$valid) {
305
        return false;
306
      }
307
308
      if (self::_stepIsInternal($step)) {
309
        $step = array(
310
            'file'     => $step['file'],
311
            'line'     => $step['line'],
312
            'function' => '',
313
        );
314
        array_unshift($trace, $step);
315
        break;
316
      }
317
      if ($step['function'] !== 'spl_autoload_call') { # meaningless
318
        array_unshift($trace, $step);
319
      }
320
    }
321
    if (!$fileFound) {
322
      return false;
323
    }
324
325
    $output = array();
326
    foreach ($trace as $step) {
327
      if (isset($step['file'])) {
328
        $file = $step['file'];
329
330
        if (isset($step['line'])) {
331
          $line = $step['line'];
332
          # include the source of this step
333
          if (self::enabled() === self::MODE_RICH) {
334
            $source = self::_showSource($file, $line);
335
          }
336
        }
337
      }
338
339
      $function = $step['function'];
340
341
      if (in_array($function, array('include', 'include_once', 'require', 'require_once'), true)) {
342
        if (empty($step['args'])) {
343
          # no arguments
344
          $args = array();
345
        } else {
346
          # sanitize the included file path
347
          $args = array('file' => self::shortenPath($step['args'][0]));
348
        }
349
      } elseif (isset($step['args'])) {
350
        if (empty($step['class']) && !function_exists($function)) {
351
          # introspection on closures or language constructs in a stack trace is impossible before PHP 5.3
352
          $params = null;
353
        } else {
354
          try {
355
            if (isset($step['class'])) {
356
              if (method_exists($step['class'], $function)) {
357
                $reflection = new \ReflectionMethod($step['class'], $function);
358
              } elseif (isset($step['type']) && $step['type'] == '::') {
359
                $reflection = new \ReflectionMethod($step['class'], '__callStatic');
360
              } else {
361
                $reflection = new \ReflectionMethod($step['class'], '__call');
362
              }
363
            } else {
364
              $reflection = new \ReflectionFunction($function);
365
            }
366
367
            # get the function parameters
368
            $params = $reflection->getParameters();
369
          } catch (\Exception $e) { # avoid various PHP version incompatibilities
370
            $params = null;
371
          }
372
        }
373
374
        $args = array();
375
        foreach ($step['args'] as $i => $arg) {
376
          if (isset($params[$i])) {
377
            # assign the argument by the parameter name
378
            $args[$params[$i]->name] = $arg;
379
          } else {
380
            # assign the argument by number
381
            $args['#' . ($i + 1)] = $arg;
382
          }
383
        }
384
      }
385
386
      if (isset($step['class'])) {
387
        # Class->method() or Class::method()
0 ignored issues
show
Unused Code Comprehensibility introduced by
65% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
388
        $function = $step['class'] . $step['type'] . $function;
389
      }
390
391
      // TODO: it's possible to parse the object name out from the source!
392
      $output[] = array(
393
          'function' => $function,
394
          'args'     => isset($args) ? $args : null,
395
          'file'     => isset($file) ? $file : null,
396
          'line'     => isset($line) ? $line : null,
397
          'source'   => isset($source) ? $source : null,
398
          'object'   => isset($step['object']) ? $step['object'] : null,
399
      );
400
401
      unset($function, $args, $file, $line, $source);
402
    }
403
404
    return $output;
405
  }
406
407
  /**
408
   * removes comments and zaps whitespace & <?php tags from php code, makes for easier further parsing
409
   *
410
   * @param string $source
411
   *
412
   * @return string
413
   */
414
  private static function _removeAllButCode($source)
415
  {
416
    $commentTokens = array(
417
        T_COMMENT     => true,
418
        T_INLINE_HTML => true,
419
        T_DOC_COMMENT => true,
420
    );
421
422
    $whiteSpaceTokens = array(
423
        T_WHITESPACE         => true,
424
        T_CLOSE_TAG          => true,
425
        T_OPEN_TAG           => true,
426
        T_OPEN_TAG_WITH_ECHO => true,
427
    );
428
429
    $cleanedSource = '';
430
    foreach (token_get_all($source) as $token) {
431
      if (is_array($token)) {
432
        if (isset($commentTokens[$token[0]])) {
433
          continue;
434
        }
435
436
        if (isset($whiteSpaceTokens[$token[0]])) {
437
          $token = "\x07";
438
        } else {
439
          $token = $token[1];
440
        }
441
      } elseif ($token === ';') {
442
        $token = "\x07";
443
      }
444
445
      $cleanedSource .= $token;
446
    }
447
448
    return $cleanedSource;
449
  }
450
451
  /**
452
   * trace helper, shows the place in code inline
453
   *
454
   * @param string $file       full path to file
455
   * @param int    $lineNumber the line to display
456
   * @param int    $padding    surrounding lines to show besides the main one
457
   *
458
   * @return bool|string
459
   */
460
  private static function _showSource($file, $lineNumber, $padding = 7)
461
  {
462
    if (
463
        !$file
464
        ||
465
        !is_readable($file)
466
    ) {
467
      # continuing will cause errors
468
      return false;
469
    }
470
471
    # open the file and set the line position
472
    $filePointer = fopen($file, 'r');
473
    $line = 0;
474
475
    # Set the reading range
476
    $range = array(
477
        'start' => $lineNumber - $padding,
478
        'end'   => $lineNumber + $padding,
479
    );
480
481
    # set the zero-padding amount for line numbers
482
    $format = '% ' . strlen($range['end']) . 'd';
483
484
    $source = '';
485
    while (($row = fgets($filePointer)) !== false) {
486
      # increment the line number
487
      if (++$line > $range['end']) {
488
        break;
489
      }
490
491
      if ($line >= $range['start']) {
492
        # make the row safe for output
493
        $row = htmlspecialchars($row, ENT_NOQUOTES, 'UTF-8');
494
495
        # trim whitespace and sanitize the row
496
        $row = '<span>' . sprintf($format, $line) . '</span> ' . $row;
497
498
        if ($line === $lineNumber) {
499
          # apply highlighting to this row
500
          $row = '<div class="kint-highlight">' . $row . '</div>';
501
        } else {
502
          $row = '<div>' . $row . '</div>';
503
        }
504
505
        # add to the captured source
506
        $source .= $row;
507
      }
508
    }
509
510
    # close the file-pointer
511
    fclose($filePointer);
512
513
    return $source;
514
  }
515
516
  /**
517
   * returns whether current trace step belongs to Kint or its wrappers
518
   *
519
   * @param $step
520
   *
521
   * @return array
522
   */
523 1
  private static function _stepIsInternal($step)
524
  {
525 1
    if (isset($step['class'])) {
526 1
      foreach (self::$aliases['methods'] as $alias) {
527 1
        if ($alias[0] === strtolower($step['class']) && $alias[1] === strtolower($step['function'])) {
528 1
          return true;
529
        }
530
      }
531
532 1
      return false;
533
    } else {
534
      return in_array(strtolower($step['function']), self::$aliases['functions'], true);
535
    }
536
  }
537
538
  /**
539
   * Dump information about variables, accepts any number of parameters, supports modifiers:
540
   *
541
   *  clean up any output before kint and place the dump at the top of page:
542
   *   - Kint::dump()
543
   *  *****
544
   *  expand all nodes on display:
545
   *   ! Kint::dump()
546
   *  *****
547
   *  dump variables disregarding their depth:
548
   *   + Kint::dump()
549
   *  *****
550
   *  return output instead of displaying it:
551
   *   @ Kint::dump()
552
   *  *****
553
   *  force output as plain text
554
   *   ~ Kint::dump()
555
   *
556
   * Modifiers are supported by all dump wrapper functions, including Kint::trace(). Space is optional.
557
   *
558
   *
559
   * You can also use the following shorthand to display debug_backtrace():
560
   *   Kint::dump( 1 );
561
   *
562
   * Passing the result from debug_backtrace() to kint::dump() as a single parameter will display it as trace too:
563
   *   $trace = debug_backtrace( true );
564
   *   Kint::dump( $trace );
565
   *  Or simply:
566
   *   Kint::dump( debug_backtrace() );
567
   *
568
   *
569
   * @param mixed $data
570
   *
571
   * @return void|string
572
   */
573 1
  public static function dump($data = null)
574
  {
575 1
    if (!self::enabled()) {
576
      return '';
577
    }
578
579 1
    $stash = self::settings();
580
581 1
    list($names, $modifiers, $callee, $previousCaller, $miniTrace) = self::_getCalleeInfo(
582 1
        defined('DEBUG_BACKTRACE_IGNORE_ARGS')
583 1
            ? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)
584 1
            : debug_backtrace()
585
    );
586
587
    # set mode for current run
588 1
    $mode = self::enabled();
589 1
    if ($mode === true) {
590
      $mode = (PHP_SAPI === 'cli' && self::$cliDetection === true) ? self::MODE_CLI : self::MODE_RICH;
591
    }
592 1
    self::enabled($mode);
593
594 1
    if (strpos($modifiers, '~') !== false) {
595
      self::enabled(self::MODE_WHITESPACE);
596
    }
597
598 1
    switch (self::enabled()) {
599 1
      case self::MODE_RICH:
600
        $decorator = 'kint\decorators\Kint_Decorators_Rich';
601
        break;
602 1
      case self::MODE_JS:
603
        $decorator = 'kint\decorators\Kint_Decorators_JS';
604
        break;
605
      default:
606 1
      case self::MODE_PLAIN:
0 ignored issues
show
Unused Code introduced by
case self::MODE_PLAIN: ...tors_Plain'; break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
607 1
        $decorator = 'kint\decorators\Kint_Decorators_Plain';
608 1
        break;
609
    }
610
611
    /* @var Kint_Decorators $decorator */
612
613 1
    $firstRunOldValue = $decorator::$firstRun;
614
615
    # process modifiers: @, +, ! and -
616 1
    if (strpos($modifiers, '-') !== false) {
617
      $decorator::$firstRun = true;
618
      while (ob_get_level()) {
619
        ob_end_clean();
620
      }
621
    }
622
623 1
    if (strpos($modifiers, '!') !== false) {
624
      self::$expandedByDefault = true;
625
    }
626
627 1
    if (strpos($modifiers, '+') !== false) {
628
      self::$maxLevels = false;
629
    }
630
631 1
    if (strpos($modifiers, '@') !== false) {
632
      self::$returnOutput = true;
633
      $decorator::$firstRun = true;
634
    }
635
636 1
    $output = '';
637 1
    if ($decorator::$firstRun) {
638 1
      $output .= call_user_func(array($decorator, 'init'));
639
    }
640
641 1
    $trace = false;
642 1
    $tmpFuncNumArgs = func_num_args();
643
    if (
644 1
        $data === 1
645
        &&
646 1
        $tmpFuncNumArgs === 1
647
        &&
648 1
        $names === array(null)
649
    ) {
650
651
      # Kint::dump(1) shorthand
0 ignored issues
show
Unused Code Comprehensibility introduced by
45% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
652
      $trace = debug_backtrace(true);
653
654
    } elseif (
655 1
        $tmpFuncNumArgs === 1
656
        &&
657 1
        is_array($data)
658
    ) {
659
660 1
      $trace = $data; # test if the single parameter is result of debug_backtrace()
661
662
    }
663 1
    $trace and $trace = self::_parseTrace($trace);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

PHP has two types of connecting operators (logical operators, and boolean operators):

  Logical Operators Boolean Operator
AND - meaning and &&
OR - meaning or ||

The difference between these is the order in which they are executed. In most cases, you would want to use a boolean operator like &&, or ||.

Let’s take a look at a few examples:

// Logical operators have lower precedence:
$f = false or true;

// is executed like this:
($f = false) or true;


// Boolean operators have higher precedence:
$f = false || true;

// is executed like this:
$f = (false || true);

Logical Operators are used for Control-Flow

One case where you explicitly want to use logical operators is for control-flow such as this:

$x === 5
    or die('$x must be 5.');

// Instead of
if ($x !== 5) {
    die('$x must be 5.');
}

Since die introduces problems of its own, f.e. it makes our code hardly testable, and prevents any kind of more sophisticated error handling; you probably do not want to use this in real-world code. Unfortunately, logical operators cannot be combined with throw at this point:

// The following is currently a parse error.
$x === 5
    or throw new RuntimeException('$x must be 5.');

These limitations lead to logical operators rarely being of use in current PHP code.

Loading history...
664
665 1
    $output .= call_user_func(array($decorator, 'wrapStart'));
666 1
    if ($trace) {
667
      $output .= call_user_func(array($decorator, 'decorateTrace'), $trace);
668
    } else {
669 1
      $data = $tmpFuncNumArgs === 0 ? array('[[no arguments passed]]') : func_get_args();
670
671 1
      foreach ($data as $k => $argument) {
672 1
        KintParser::reset();
673
        # when the dump arguments take long to generate output, user might have changed the file and
674
        # Kint might not parse the arguments correctly, so check if names are set and while the
675
        # displayed names might be wrong, at least don't throw an error
676 1
        $output .= call_user_func(
677 1
            array($decorator, 'decorate'),
678 1
            KintParser::factory($argument, isset($names[$k]) ? $names[$k] : '')
679
        );
680
      }
681
    }
682
683 1
    $output .= call_user_func(array($decorator, 'wrapEnd'), $callee, $miniTrace, $previousCaller);
684
685 1
    $decorator::$firstRun = false;
686
687 1
    if (strpos($modifiers, '@') !== false) {
688
      $decorator::$firstRun = $firstRunOldValue;
689
    }
690
691 1
    if (self::$returnOutput) {
692
      self::settings($stash);
693
694
      return $output;
695
    }
696
697 1
    if (self::$delayedMode) {
698
      self::settings($stash);
699
      register_shutdown_function('printf', '%s', $output);
700
701
      return '';
702
    }
703
704 1
    self::settings($stash);
705 1
    echo $output;
706
707 1
    return '';
708
  }
709
710
  /**
711
   * Enables or disables Kint, can globally enforce the rendering mode. If called without parameters, returns the
712
   * current mode.
713
   *
714
   * @param mixed $forceMode
715
   *                     null or void - return current mode
716
   *                     false        - disable (no output)
717
   *                     true         - enable and detect cli automatically
718
   *                     Kint::MODE_* - enable and force selected mode disregarding detection and function
719
   *                     shorthand (s()/d()), note that you can still override this
720
   *                     with the "~" modifier
721
   *
722
   * @return mixed        previously set value if a new one is passed
723
   */
724 1
  public static function enabled($forceMode = null)
725
  {
726
    # act both as a setter...
727 1
    if (isset($forceMode)) {
728 1
      $before = self::$_enabledMode;
729 1
      self::$_enabledMode = $forceMode;
730
731 1
      return $before;
732
    }
733
734
    # ...and a getter
735 1
    return self::$_enabledMode;
736
  }
737
738
  /**
739
   * Stashes or sets all settings at once
740
   *
741
   * @param array|null $settings Array of all settings to be set or null to set none
742
   *
743
   * @return array Current settings
744
   */
745 1
  public static function settings(array $settings = null)
746
  {
747 1
    static $keys = array(
748
        'delayedMode',
749
        '_enabledMode',
750
        'aliases',
751
        'appRootDirs',
752
        'cliColors',
753
        'displayCalledFrom',
754
        'expandedByDefault',
755
        'fileLinkFormat',
756
        'maxLevels',
757
        'maxStrLength',
758
        'returnOutput',
759
        'theme',
760
    );
761
762 1
    $out = array();
763 1
    foreach ($keys as $key) {
764
      /** @noinspection PhpVariableVariableInspection */
765 1
      $out[$key] = self::$$key;
766
    }
767
768 1
    if ($settings !== null) {
769 1
      $in = array_intersect_key($settings, array_flip($keys));
770 1
      foreach ($in as $key => $val) {
771
        /** @noinspection PhpVariableVariableInspection */
772 1
        self::$$key = $val;
773
      }
774
    }
775
776 1
    return $out;
777
  }
778
779
  /**
780
   * @param string $file
781
   * @param int    $line
782
   *
783
   * @return mixed
784
   */
785
  public static function getIdeLink($file, $line)
786
  {
787
    return str_replace(array('%f', '%l'), array($file, $line), self::$fileLinkFormat);
788
  }
789
790
  /**
791
   * generic path display callback, can be configured in the settings; purpose is to show relevant path info and hide
792
   * as much of the path as possible.
793
   *
794
   * @param string $file
795
   *
796
   * @return string
797
   */
798
  public static function shortenPath($file)
799
  {
800
    $file = str_replace('\\', '/', $file);
801
    $shortenedName = $file;
802
    $replaced = false;
803
    if (is_array(self::$appRootDirs)) {
804
      foreach (self::$appRootDirs as $path => $replaceString) {
805
        if (empty($path)) {
806
          continue;
807
        }
808
809
        $path = str_replace('\\', '/', $path);
810
811
        if (strpos($file, $path) === 0) {
812
          $shortenedName = $replaceString . substr($file, strlen($path));
813
          $replaced = true;
814
          break;
815
        }
816
      }
817
    }
818
819
    # fallback to find common path with Kint dir
820
    if (!$replaced) {
821
      $pathParts = explode('/', str_replace('\\', '/', KINT_DIR));
822
      $fileParts = explode('/', $file);
823
      $i = 0;
824
      foreach ($fileParts as $i => $filePart) {
825
        if (!isset($pathParts[$i]) || $pathParts[$i] !== $filePart) {
826
          break;
827
        }
828
      }
829
830
      $shortenedName = ($i ? '.../' : '') . implode('/', array_slice($fileParts, $i));
831
    }
832
833
    return $shortenedName;
834
  }
835
836
  /**
837
   * Prints a debug backtrace, same as Kint::dump(1)
838
   *
839
   * @param array $trace [OPTIONAL] you can pass your own trace, otherwise, `debug_backtrace` will be called
840
   *
841
   * @return mixed
842
   */
843
  public static function trace($trace = null)
844
  {
845
    if (!self::enabled()) {
846
      return '';
847
    }
848
849
    return self::dump(isset($trace) ? $trace : debug_backtrace(true));
850
  }
851
}
852