Completed
Push — master ( 6c6127...e8b87e )
by Lars
02:36
created

Kint   D

Complexity

Total Complexity 138

Size/Duplication

Total Lines 843
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 2

Test Coverage

Coverage 27.89%

Importance

Changes 8
Bugs 2 Features 3
Metric Value
wmc 138
c 8
b 2
f 3
lcom 2
cbo 2
dl 0
loc 843
ccs 111
cts 398
cp 0.2789
rs 4.4444

11 Methods

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

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 2
  private static function _getCalleeInfo($trace)
79
  {
80 2
    $previousCaller = array();
81 2
    $miniTrace = array();
82 2
    $prevStep = array();
83
84
    # go from back of trace to find first occurrence of call to Kint or its wrappers
85
    /** @noinspection PhpAssignmentInConditionInspection */
86 2
    while ($step = array_pop($trace)) {
87
88 2
      if (self::_stepIsInternal($step)) {
89
        $previousCaller = $prevStep;
90
        break;
91 2
      } elseif (isset($step['file'], $step['line'])) {
92 2
        unset($step['object'], $step['args']);
93 2
        array_unshift($miniTrace, $step);
94 2
      }
95
96 2
      $prevStep = $step;
97 2
    }
98 2
    $callee = $step;
99
100
    if (
101 2
        !isset($callee['file'])
102 2
        ||
103
        !is_readable($callee['file'])
104 2
    ) {
105 2
      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 2
  private static function _parseTrace(array $data)
282
  {
283 2
    $trace = array();
284 2
    $traceFields = array('file', 'line', 'args', 'class');
285 2
    $fileFound = false; # file element must exist in one of the steps
286
287
    # validate whether a trace was indeed passed
288
    /** @noinspection PhpAssignmentInConditionInspection */
289 2
    while ($step = array_pop($data)) {
290 2
      if (!is_array($step) || !isset($step['function'])) {
291 2
        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 2
  private static function _stepIsInternal($step)
524
  {
525 2
    if (isset($step['class'])) {
526 2
      foreach (self::$aliases['methods'] as $alias) {
527
        if (
528 2
            $alias[0] === $step['class']
529 2
            &&
530
            $alias[1] === $step['function']
531 2
        ) {
532
          return true;
533
        }
534 2
      }
535
536 2
      return false;
537
    } else {
538
      return in_array($step['function'], self::$aliases['functions'], true);
539
    }
540
  }
541
542
  /**
543
   * Dump information about variables, accepts any number of parameters, supports modifiers:
544
   *
545
   *  clean up any output before kint and place the dump at the top of page:
546
   *   - Kint::dump()
547
   *  *****
548
   *  expand all nodes on display:
549
   *   ! Kint::dump()
550
   *  *****
551
   *  dump variables disregarding their depth:
552
   *   + Kint::dump()
553
   *  *****
554
   *  return output instead of displaying it:
555
   *   @ Kint::dump()
556
   *  *****
557
   *  force output as plain text
558
   *   ~ Kint::dump()
559
   *
560
   * Modifiers are supported by all dump wrapper functions, including Kint::trace(). Space is optional.
561
   *
562
   *
563
   * You can also use the following shorthand to display debug_backtrace():
564
   *   Kint::dump( 1 );
565
   *
566
   * Passing the result from debug_backtrace() to kint::dump() as a single parameter will display it as trace too:
567
   *   $trace = debug_backtrace( true );
568
   *   Kint::dump( $trace );
569
   *  Or simply:
570
   *   Kint::dump( debug_backtrace() );
571
   *
572
   *
573
   * @param mixed $data
574
   *
575
   * @return void|string
576
   */
577 2
  public static function dump($data = null)
578
  {
579 2
    if (!self::enabled()) {
580
      return '';
581
    }
582
583 2
    $stash = self::settings();
584
585 2
    list($names, $modifiers, $callee, $previousCaller, $miniTrace) = self::_getCalleeInfo(
586 2
        defined('DEBUG_BACKTRACE_IGNORE_ARGS')
587 2
            ? debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)
588 2
            : debug_backtrace()
589 2
    );
590
591
    # set mode for current run
592 2
    $mode = self::enabled();
593 2
    if ($mode === true) {
594
      $mode = (PHP_SAPI === 'cli' && self::$cliDetection === true) ? self::MODE_CLI : self::MODE_RICH;
595
    }
596 2
    self::enabled($mode);
597
598 2
    if (strpos($modifiers, '~') !== false) {
599
      self::enabled(self::MODE_WHITESPACE);
600
    }
601
602 2
    switch (self::enabled()) {
603 2
      case self::MODE_RICH:
604 1
        $decorator = 'kint\decorators\Kint_Decorators_Rich';
605 1
        break;
606 1
      case self::MODE_JS:
607
        $decorator = 'kint\decorators\Kint_Decorators_JS';
608
        break;
609 1
      default:
610 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...
611 1
        $decorator = 'kint\decorators\Kint_Decorators_Plain';
612 1
        break;
613 2
    }
614
615
    /* @var Kint_Decorators $decorator */
616
617 2
    $firstRunOldValue = $decorator::$firstRun;
618
619
    # process modifiers: @, +, ! and -
620 2
    if (strpos($modifiers, '-') !== false) {
621
      $decorator::$firstRun = true;
622
      while (ob_get_level()) {
623
        ob_end_clean();
624
      }
625
    }
626
627 2
    if (strpos($modifiers, '!') !== false) {
628
      self::$expandedByDefault = true;
629
    }
630
631 2
    if (strpos($modifiers, '+') !== false) {
632
      self::$maxLevels = false;
633
    }
634
635 2
    if (strpos($modifiers, '@') !== false) {
636
      self::$returnOutput = true;
637
      $decorator::$firstRun = true;
638
    }
639
640 2
    $output = '';
641 2
    if ($decorator::$firstRun) {
642 1
      $output .= call_user_func(array($decorator, 'init'));
643 1
    }
644
645 2
    $trace = false;
646 2
    $tmpFuncNumArgs = func_num_args();
647
    if (
648
        $data === 1
649 2
        &&
650
        $tmpFuncNumArgs === 1
651 2
        &&
652
        $names === array(null)
653 2
    ) {
654
655
      # 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...
656
      $trace = debug_backtrace(true);
657
658
    } elseif (
659
        $tmpFuncNumArgs === 1
660 2
        &&
661 2
        is_array($data)
662 2
    ) {
663
664 2
      $trace = $data; # test if the single parameter is result of debug_backtrace()
665
666 2
    }
667 2
    $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...
668
669 2
    $output .= call_user_func(array($decorator, 'wrapStart'));
670 2
    if ($trace) {
671
      $output .= call_user_func(array($decorator, 'decorateTrace'), $trace);
672
    } else {
673 2
      $data = $tmpFuncNumArgs === 0 ? array('[[no arguments passed]]') : func_get_args();
674
675 2
      foreach ($data as $k => $argument) {
676 2
        KintParser::reset();
677
        # when the dump arguments take long to generate output, user might have changed the file and
678
        # Kint might not parse the arguments correctly, so check if names are set and while the
679
        # displayed names might be wrong, at least don't throw an error
680 2
        $output .= call_user_func(
681 2
            array($decorator, 'decorate'),
682 2
            KintParser::factory($argument, isset($names[$k]) ? $names[$k] : '')
683 2
        );
684 2
      }
685
    }
686
687 2
    $output .= call_user_func(array($decorator, 'wrapEnd'), $callee, $miniTrace, $previousCaller);
688
689 2
    $decorator::$firstRun = false;
690
691 2
    if (strpos($modifiers, '@') !== false) {
692
      $decorator::$firstRun = $firstRunOldValue;
693
    }
694
695 2
    if (self::$returnOutput) {
696
      self::settings($stash);
697
698
      return $output;
699
    }
700
701 2
    if (self::$delayedMode) {
702
      self::settings($stash);
703
      register_shutdown_function('printf', '%s', $output);
704
705
      return '';
706
    }
707
708 2
    self::settings($stash);
709 2
    echo $output;
710
711 2
    return '';
712
  }
713
714
  /**
715
   * Enables or disables Kint, can globally enforce the rendering mode. If called without parameters, returns the
716
   * current mode.
717
   *
718
   * @param mixed $forceMode
719
   *                     null or void - return current mode
720
   *                     false        - disable (no output)
721
   *                     true         - enable and detect cli automatically
722
   *                     Kint::MODE_* - enable and force selected mode disregarding detection and function
723
   *                     shorthand (s()/d()), note that you can still override this
724
   *                     with the "~" modifier
725
   *
726
   * @return mixed        previously set value if a new one is passed
727
   */
728 2
  public static function enabled($forceMode = null)
729
  {
730
    # act both as a setter...
731 2
    if (isset($forceMode)) {
732 2
      $before = self::$_enabledMode;
733 2
      self::$_enabledMode = $forceMode;
734
735 2
      return $before;
736
    }
737
738
    # ...and a getter
739 2
    return self::$_enabledMode;
740
  }
741
742
  /**
743
   * Stashes or sets all settings at once
744
   *
745
   * @param array|null $settings Array of all settings to be set or null to set none
746
   *
747
   * @return array Current settings
748
   */
749 2
  public static function settings(array $settings = null)
750
  {
751
    static $keys = array(
752
        'delayedMode',
753
        '_enabledMode',
754
        'aliases',
755
        'appRootDirs',
756
        'cliColors',
757
        'displayCalledFrom',
758
        'expandedByDefault',
759
        'fileLinkFormat',
760
        'maxLevels',
761
        'maxStrLength',
762
        'returnOutput',
763
        'theme',
764 2
    );
765
766 2
    $out = array();
767 2
    foreach ($keys as $key) {
768
      /** @noinspection PhpVariableVariableInspection */
769 2
      $out[$key] = self::$$key;
770 2
    }
771
772 2
    if ($settings !== null) {
773 2
      $in = array_intersect_key($settings, array_flip($keys));
774 2
      foreach ($in as $key => $val) {
775
        /** @noinspection PhpVariableVariableInspection */
776 2
        self::$$key = $val;
777 2
      }
778 2
    }
779
780 2
    return $out;
781
  }
782
783
  /**
784
   * @param string $file
785
   * @param int    $line
786
   *
787
   * @return mixed
788
   */
789
  public static function getIdeLink($file, $line)
790
  {
791
    return str_replace(array('%f', '%l'), array($file, $line), self::$fileLinkFormat);
792
  }
793
794
  /**
795
   * generic path display callback, can be configured in the settings; purpose is to show relevant path info and hide
796
   * as much of the path as possible.
797
   *
798
   * @param string $file
799
   *
800
   * @return string
801
   */
802
  public static function shortenPath($file)
803
  {
804
    $file = str_replace('\\', '/', $file);
805
    $shortenedName = $file;
806
    $replaced = false;
807
    if (is_array(self::$appRootDirs)) {
808
      foreach (self::$appRootDirs as $path => $replaceString) {
809
        if (empty($path)) {
810
          continue;
811
        }
812
813
        $path = str_replace('\\', '/', $path);
814
815
        if (strpos($file, $path) === 0) {
816
          $shortenedName = $replaceString . substr($file, strlen($path));
817
          $replaced = true;
818
          break;
819
        }
820
      }
821
    }
822
823
    # fallback to find common path with Kint dir
824
    if (!$replaced) {
825
      $pathParts = explode('/', str_replace('\\', '/', KINT_DIR));
826
      $fileParts = explode('/', $file);
827
      $i = 0;
828
      foreach ($fileParts as $i => $filePart) {
829
        if (!isset($pathParts[$i]) || $pathParts[$i] !== $filePart) {
830
          break;
831
        }
832
      }
833
834
      $shortenedName = ($i ? '.../' : '') . implode('/', array_slice($fileParts, $i));
835
    }
836
837
    return $shortenedName;
838
  }
839
840
  /**
841
   * Prints a debug backtrace, same as Kint::dump(1)
842
   *
843
   * @param array $trace [OPTIONAL] you can pass your own trace, otherwise, `debug_backtrace` will be called
844
   *
845
   * @return mixed
846
   */
847
  public static function trace($trace = null)
848
  {
849
    if (!self::enabled()) {
850
      return '';
851
    }
852
853
    return self::dump(isset($trace) ? $trace : debug_backtrace(true));
854
  }
855
}
856