Completed
Push — master ( d4087b...5d9c5a )
by Lars
03:14
created

KintParser::factory()   D

Complexity

Conditions 16
Paths 36

Size

Total Lines 112
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 79.132

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 112
rs 4.8736
ccs 22
cts 59
cp 0.3729
cc 16
eloc 63
nc 36
nop 2
crap 79.132

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace kint\inc;
4
5
use kint\decorators\Kint_Decorators_Rich;
6
use kint\Kint;
7
use voku\helper\UTF8;
8
9
/**
10
 * Class KintParser
11
 */
12
abstract class KintParser extends KintVariableData
13
{
14
  /**
15
   * @var int
16
   */
17
  private static $_level = 0;
18
19
  /**
20
   * @var bool
21
   */
22
  private static $_dealingWithGlobals = false;
23
24
  /**
25
   * @var bool
26
   */
27
  private static $_placeFullStringInValue = false;
28
29
  /**
30
   * @var array
31
   */
32
  private static $_customDataTypes = array(
33
      'ClassMethods',
34
      'ClassStatics',
35
      'Color',
36
      'FsPath',
37
      'Json',
38
      'Microtime',
39
      'ObjectIterateable',
40
      'SplObjectStorage',
41
      'Timestamp',
42
      'Xml',
43
  );
44
45
  /**
46
   * @var array
47
   */
48
  private static $_objectParsers = array(
49
      'Closure',
50
      'Smarty',
51
      'SplFileInfo',
52
  );
53
54
  /**
55
   * @var array
56
   */
57
  private static $_objects = array();
58
59
  /**
60
   * @var mixed
61
   */
62
  private static $_marker;
63
64
  /**
65
   * @var bool
66
   */
67
  private static $_skipAlternatives = false;
68
69
  /**
70
   * @return bool
71
   */
72 1
  private static function _checkDepth()
73
  {
74 1
    return Kint::$maxLevels != 0 && self::$_level >= Kint::$maxLevels;
75
  }
76
77
  /**
78
   * @param KintVariableData $kintVar
79
   *
80
   * @return string
81
   */
82
  private static function _decorateCell(KintVariableData $kintVar)
83
  {
84
    if ($kintVar->extendedValue !== null || !empty($kintVar->_alternatives)) {
85
      return '<td>' . Kint_Decorators_Rich::decorate($kintVar) . '</td>';
86
    }
87
88
    $output = '<td';
89
90
    if ($kintVar->value !== null) {
91
      $output .= ' title="' . $kintVar->type;
92
93
      if ($kintVar->size !== null) {
94
        $output .= " (" . $kintVar->size . ")";
95
      }
96
97
      $output .= '">' . $kintVar->value;
98
    } else {
99
      $output .= '>';
100
101
      if ($kintVar->type !== 'NULL') {
102
        $output .= '<u>' . $kintVar->type;
103
104
        if ($kintVar->size !== null) {
105
          $output .= "(" . $kintVar->size . ")";
106
        }
107
108
        $output .= '</u>';
109
      } else {
110
        $output .= '<u>NULL</u>';
111
      }
112
    }
113
114
115
    return $output . '</td>';
116
  }
117
118
  /**
119
   * @param array $variable
120
   *
121
   * @return array|bool
122
   */
123 1
  private static function _isArrayTabular(array &$variable)
124
  {
125 1
    if (Kint::enabled() !== Kint::MODE_RICH) {
126 1
      return false;
127
    }
128
129
    $arrayKeys = array();
130
    $keys = null;
131
    $closeEnough = false;
132
    foreach ($variable as $row) {
133
      if (!is_array($row) || empty($row)) {
134
        return false;
135
      }
136
137
      foreach ($row as $col) {
138
        if (!empty($col) && !is_scalar($col)) {
139
          return false;
140
        } // todo add tabular "tolerance"
141
      }
142
143
      if (isset($keys) && !$closeEnough) {
144
        # let's just see if the first two rows have same keys, that's faster and has the
145
        # positive side effect of easily spotting missing keys in later rows
146
        if ($keys !== array_keys($row)) {
147
          return false;
148
        }
149
150
        $closeEnough = true;
151
      } else {
152
        $keys = array_keys($row);
153
      }
154
155
      $arrayKeys = array_unique(array_merge($arrayKeys, $keys));
156
    }
157
158
    return $arrayKeys;
159
  }
160
161
  /**
162
   * main and usually single method a custom parser must implement
163
   *
164
   * @param mixed $variable
165
   *
166
   * @return mixed [!!!] false is returned if the variable is not of current type
167
   */
168
  abstract protected function _parse(&$variable);
169
170
  /** @noinspection PhpUnusedPrivateMethodInspection */
171
  /**
172
   * @param mixed            $variable
173
   * @param KintVariableData $variableData
174
   *
175
   * @return bool|void
176
   */
177 1
  private static function _parse_array(&$variable, KintVariableData $variableData)
178
  {
179 1
    isset(self::$_marker) or self::$_marker = "\x00" . uniqid('kint', true);
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as or 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...
180
181
    # naturally, $GLOBALS variable is an intertwined recursion nightmare, use black magic
182 1
    $globalsDetector = false;
183
    if (
184 1
        isset($variable['GLOBALS'])
185
        &&
186 1
        is_array($variable['GLOBALS'])
187
    ) {
188
      $globalsDetector = "\x01" . uniqid('kint', true);
189
190
      $variable['GLOBALS'][$globalsDetector] = true;
191
      if (isset($variable[$globalsDetector])) {
192
        unset($variable[$globalsDetector]);
193
        self::$_dealingWithGlobals = true;
194
      } else {
195
        unset($variable['GLOBALS'][$globalsDetector]);
196
        $globalsDetector = false;
197
      }
198
    }
199
200 1
    $variableData->type = 'array';
201 1
    $variableData->size = count($variable);
202
203 1
    if ($variableData->size === 0) {
204
      /** @noinspection PhpInconsistentReturnPointsInspection */
205
      return;
206
    }
207
208 1
    if (isset($variable[self::$_marker])) { # recursion; todo mayhaps show from where
209
      if (self::$_dealingWithGlobals) {
210
        $variableData->value = '*RECURSION*';
211
      } else {
212
        unset($variable[self::$_marker]);
213
        $variableData->value = self::$_marker;
214
      }
215
216
      return false;
217
    }
218 1
    if (self::_checkDepth()) {
219
      $variableData->extendedValue = "*DEPTH TOO GREAT*";
0 ignored issues
show
Documentation Bug introduced by
It seems like '*DEPTH TOO GREAT*' of type string is incompatible with the declared type array<integer,object<kint\inc\KintVariableData>> of property $extendedValue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
220
221
      return false;
222
    }
223
224 1
    $isSequential = self::_isSequential($variable);
225
226
    if (
227 1
        $variableData->size > 1
228
        &&
229 1
        ($arrayKeys = self::_isArrayTabular($variable)) !== false
230
    ) {
231
      $variable[self::$_marker] = true; # this must be AFTER _isArrayTabular
232
      $firstRow = true;
233
      $extendedValue = '<table class="kint-report"><thead>';
234
235
      foreach ($variable as $rowIndex => &$row) {
236
        # display strings in their full length
237
        self::$_placeFullStringInValue = true;
238
239
        if ($rowIndex === self::$_marker) {
240
          continue;
241
        }
242
243
        if (isset($row[self::$_marker])) {
244
          $variableData->value = "*RECURSION*";
245
246
          return false;
247
        }
248
249
        $extendedValue .= '<tr>';
250
        if ($isSequential) {
251
          $output = '<td>' . '#' . ($rowIndex + 1) . '</td>';
252
        } else {
253
          $output = self::_decorateCell(KintParser::factory($rowIndex));
254
        }
255
        if ($firstRow) {
256
          $extendedValue .= '<th>&nbsp;</th>';
257
        }
258
259
        # we iterate the known full set of keys from all rows in case some appeared at later rows,
260
        # as we only check the first two to assume
261
        foreach ($arrayKeys as $key) {
262
          if ($firstRow) {
263
            $extendedValue .= '<th>' . self::escape($key) . '</th>';
264
          }
265
266
          if (!isset($row[$key])) {
267
            $output .= '<td class="kint-empty"></td>';
268
            continue;
269
          }
270
271
          $var = KintParser::factory($row[$key]);
272
273
          if ($var->value === self::$_marker) {
274
            $variableData->value = '*RECURSION*';
275
276
            return false;
277
          } elseif ($var->value === '*RECURSION*') {
278
            $output .= '<td class="kint-empty"><u>*RECURSION*</u></td>';
279
          } else {
280
            $output .= self::_decorateCell($var);
281
          }
282
          unset($var);
283
        }
284
285
        if ($firstRow) {
286
          $extendedValue .= '</tr></thead><tr>';
287
          $firstRow = false;
288
        }
289
290
        $extendedValue .= $output . '</tr>';
291
      }
292
      unset($row);
293
      self::$_placeFullStringInValue = false;
294
295
      $variableData->extendedValue = $extendedValue . '</table>';
0 ignored issues
show
Documentation Bug introduced by
It seems like $extendedValue . '</table>' of type string is incompatible with the declared type array<integer,object<kint\inc\KintVariableData>> of property $extendedValue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
296
297
    } else {
298 1
      $variable[self::$_marker] = true;
299 1
      $extendedValue = array();
300
301 1
      foreach ($variable as $key => &$val) {
302 1
        if ($key === self::$_marker) {
303 1
          continue;
304
        }
305
306 1
        $output = KintParser::factory($val);
307 1
        if ($output->value === self::$_marker) {
308
          $variableData->value = "*RECURSION*"; // recursion occurred on a higher level, thus $this is recursion
309
          return false;
310
        }
311 1
        if (!$isSequential) {
312
          $output->operator = '=>';
313
        }
314 1
        $output->name = $isSequential ? null : "'" . $key . "'";
315 1
        $extendedValue[] = $output;
316
      }
317 1
      unset($val);
318 1
      $variableData->extendedValue = $extendedValue;
319
    }
320
321 1
    if ($globalsDetector) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $globalsDetector 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...
322
      self::$_dealingWithGlobals = false;
323
    }
324
325 1
    unset($variable[self::$_marker]);
326
327 1
    return true;
328
  }
329
330
  /** @noinspection PhpUnusedPrivateMethodInspection */
331
  /**
332
   * @param mixed            $variable
333
   * @param KintVariableData $variableData
334
   */
335
  private static function _parse_boolean(&$variable, KintVariableData $variableData)
336
  {
337
    $variableData->type = 'bool';
338
    $variableData->value = $variable ? 'TRUE' : 'FALSE';
339
  }
340
341
  /** @noinspection PhpUnusedPrivateMethodInspection */
342
  /**
343
   * @param mixed            $variable
344
   * @param KintVariableData $variableData
345
   */
346 1
  private static function _parse_double(&$variable, KintVariableData $variableData)
347
  {
348 1
    $variableData->type = 'float';
349
    /** @noinspection ReferenceMismatchInspection */
350 1
    $variableData->value = $variable;
351 1
  }
352
353
  /** @noinspection PhpUnusedPrivateMethodInspection */
354
  /**
355
   * @param mixed            $variable
356
   * @param KintVariableData $variableData
357
   */
358 1
  private static function _parse_integer(&$variable, KintVariableData $variableData)
359
  {
360 1
    $variableData->type = 'integer';
361
    /** @noinspection ReferenceMismatchInspection */
362 1
    $variableData->value = $variable;
363 1
  }
364
365
366
  /** @noinspection PhpUnusedPrivateMethodInspection */
367
  /**
368
   * @param mixed            $variable
369
   * @param KintVariableData $variableData
370
   */
371 1
  private static function _parse_null(/** @noinspection PhpUnusedParameterInspection */
372
      &$variable, KintVariableData $variableData)
0 ignored issues
show
Unused Code introduced by
The parameter $variable is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
373
  {
374 1
    $variableData->type = 'NULL';
375 1
  }
376
377
  /** @noinspection PhpUnusedPrivateMethodInspection */
378
  /**
379
   * @param mixed            $variable
380
   * @param KintVariableData $variableData
381
   *
382
   * @return bool
383
   */
384 1
  private static function _parse_object(&$variable, KintVariableData $variableData)
385
  {
386 1
    if (function_exists('spl_object_hash')) {
387 1
      $hash = spl_object_hash($variable);
388
    } else {
389
      ob_start();
390
      /** @noinspection ForgottenDebugOutputInspection */
391
      /** @noinspection ReferenceMismatchInspection */
392
      var_dump($variable);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($variable); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
393
      preg_match('[#(\d+)]', ob_get_clean(), $match);
394
      $hash = $match[1];
395
    }
396
397 1
    $castedArray = (array)$variable;
398 1
    $variableData->type = get_class($variable);
399 1
    $variableData->size = count($castedArray);
400
401 1
    if (isset(self::$_objects[$hash])) {
402
      $variableData->value = '*RECURSION*';
403
404
      return false;
405
    }
406 1
    if (self::_checkDepth()) {
407
      $variableData->extendedValue = "*DEPTH TOO GREAT*";
0 ignored issues
show
Documentation Bug introduced by
It seems like '*DEPTH TOO GREAT*' of type string is incompatible with the declared type array<integer,object<kint\inc\KintVariableData>> of property $extendedValue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
408
409
      return false;
410
    }
411
412
    # ArrayObject (and maybe ArrayIterator, did not try yet) unsurprisingly consist of mainly dark magic.
413
    # What bothers me most, var_dump sees no problem with it, and ArrayObject also uses a custom,
414
    # undocumented serialize function, so you can see the properties in internal functions, but
415
    # can never iterate some of them if the flags are not STD_PROP_LIST. Fun stuff.
416
    if (
417 1
        $variableData->type === 'ArrayObject'
418
        ||
419 1
        is_subclass_of($variable, 'ArrayObject')
420
    ) {
421
422
      /* @var $variable \ArrayObject */
423
424
      $arrayObjectFlags = $variable->getFlags();
425
      $variable->setFlags(\ArrayObject::STD_PROP_LIST);
426
    }
427
428 1
    self::$_objects[$hash] = true; // todo store reflectorObject here for alternatives cache
429 1
    $reflector = new \ReflectionObject($variable);
430
431
    # add link to definition of user-land objects
432
    if (
433 1
        Kint::$fileLinkFormat
434
        &&
435 1
        Kint::enabled() === Kint::MODE_RICH
436
        &&
437 1
        $reflector->isUserDefined()
438
    ) {
439
      $url = Kint::getIdeLink($reflector->getFileName(), $reflector->getStartLine());
440
441
      $class = (strpos($url, 'http://') === 0) ? 'class="kint-ide-link" ' : '';
442
      $variableData->type = "<a {$class}href=\"{$url}\">{$variableData->type}</a>";
443
    }
444 1
    $variableData->size = 0;
445
446 1
    $extendedValue = array();
447 1
    $encountered = array();
448
449
    # copy the object as an array as it provides more info than Reflection (depends)
450 1
    foreach ($castedArray as $key => $value) {
451
      /* casting object to array:
452
       * integer properties are inaccessible;
453
       * private variables have the class name prepended to the variable name;
454
       * protected variables have a '*' prepended to the variable name.
455
       * These prepended values have null bytes on either side.
456
       * http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
457
       */
458
      if ($key[0] === "\x00") {
459
460
        $access = $key[1] === "*" ? "protected" : "private";
461
462
        // Remove the access level from the variable name.
463
        $key = substr($key, strrpos($key, "\x00") + 1);
464
      } else {
465
        $access = "public";
466
      }
467
468
      $encountered[$key] = true;
469
470
      $output = KintParser::factory($value, self::escape($key));
471
      $output->access = $access;
472
      $output->operator = '->';
473
      $extendedValue[] = $output;
474
      $variableData->size++;
475
    }
476
477 1
    foreach ($reflector->getProperties() as $property) {
478
      $name = $property->name;
479
      if (
480
          isset($encountered[$name])
481
          ||
482
          $property->isStatic()
483
      ) {
484
        continue;
485
      }
486
487 View Code Duplication
      if ($property->isProtected()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
488
        $property->setAccessible(true);
489
        $access = "protected";
490
      } elseif ($property->isPrivate()) {
491
        $property->setAccessible(true);
492
        $access = "private";
493
      } else {
494
        $access = "public";
495
      }
496
497
      $value = $property->getValue($variable);
498
499
      $output = KintParser::factory($value, self::escape($name));
500
      $output->access = $access;
501
      $output->operator = '->';
502
      $extendedValue[] = $output;
503
      $variableData->size++;
504
    }
505
506 1
    if (isset($arrayObjectFlags)) {
507
      $variable->setFlags($arrayObjectFlags);
508
    }
509
510 1
    if ($variableData->size) {
511
      $variableData->extendedValue = $extendedValue;
512
    }
513
514 1
    return true;
515
  }
516
517
  /** @noinspection PhpUnusedPrivateMethodInspection */
518
  /**
519
   * @param mixed            $variable
520
   * @param KintVariableData $variableData
521
   */
522
  private static function _parse_resource(&$variable, KintVariableData $variableData)
523
  {
524
    $resourceType = get_resource_type($variable);
525
    $variableData->type = "resource ({$resourceType})";
526
527
    if (
528
        $resourceType === 'stream'
529
        &&
530
        $meta = stream_get_meta_data($variable)
531
    ) {
532
533
      /** @noinspection NestedPositiveIfStatementsInspection */
534
      if (isset($meta['uri'])) {
535
        $file = $meta['uri'];
536
537
        if (function_exists('stream_is_local')) {
538
          // Only exists on PHP >= 5.2.4
539
          if (stream_is_local($file)) {
540
            $file = Kint::shortenPath($file);
541
          }
542
        }
543
544
        $variableData->value = $file;
545
      }
546
    }
547
  }
548
549
  /** @noinspection PhpUnusedPrivateMethodInspection */
550
  /**
551
   * @param mixed            $variable
552
   * @param KintVariableData $variableData
553
   */
554 1
  private static function _parse_string(&$variable, KintVariableData $variableData)
555
  {
556 1
    $variableData->type = 'string';
557
558 1
    $encoding = self::_detectEncoding($variable);
559 1
    if ($encoding) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encoding of type false|string 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...
560 1
      $variableData->type .= ' [' . $encoding . ']';
561
    }
562
563 1
    $variableData->size = UTF8::strlen($variable);
564 1
    if (Kint::enabled() !== Kint::MODE_RICH) {
565 1
      $variableData->value = '"' . self::escape($variable, $encoding) . '"';
0 ignored issues
show
Security Bug introduced by
It seems like $encoding defined by self::_detectEncoding($variable) on line 558 can also be of type false; however, kint\inc\KintParser::escape() does only seem to accept null|string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
566
567 1
      return;
568
    }
569
570
    if (!self::$_placeFullStringInValue) {
571
572
      $strippedString = preg_replace('[\s+]', ' ', $variable);
573
      if (
574
          Kint::$maxStrLength 
575
          && 
576
          $variableData->size > Kint::$maxStrLength
577
      ) {
578
579
        $tmpStrippedString = self::_substr($strippedString, 0, Kint::$maxStrLength, $encoding);
580
581
        // encode and truncate
582
        $variableData->value = '"' . self::escape($tmpStrippedString, $encoding) . '&hellip;"';
0 ignored issues
show
Security Bug introduced by
It seems like $encoding defined by self::_detectEncoding($variable) on line 558 can also be of type false; however, kint\inc\KintParser::escape() does only seem to accept null|string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
583
        $variableData->extendedValue = self::escape($variable, $encoding);
0 ignored issues
show
Documentation Bug introduced by
It seems like self::escape($variable, $encoding) of type * is incompatible with the declared type array<integer,object<kint\inc\KintVariableData>> of property $extendedValue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
Security Bug introduced by
It seems like $encoding defined by self::_detectEncoding($variable) on line 558 can also be of type false; however, kint\inc\KintParser::escape() does only seem to accept null|string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
584
585
        return;
586
      } elseif ($variable !== $strippedString) { // omit no data from display
587
588
        $variableData->value = '"' . self::escape($variable, $encoding) . '"';
0 ignored issues
show
Security Bug introduced by
It seems like $encoding defined by self::_detectEncoding($variable) on line 558 can also be of type false; however, kint\inc\KintParser::escape() does only seem to accept null|string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
589
        $variableData->extendedValue = self::escape($variable, $encoding);
0 ignored issues
show
Security Bug introduced by
It seems like $encoding defined by self::_detectEncoding($variable) on line 558 can also be of type false; however, kint\inc\KintParser::escape() does only seem to accept null|string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
590
591
        return;
592
      }
593
    }
594
595
    $variableData->value = '"' . self::escape($variable, $encoding) . '"';
0 ignored issues
show
Security Bug introduced by
It seems like $encoding defined by self::_detectEncoding($variable) on line 558 can also be of type false; however, kint\inc\KintParser::escape() does only seem to accept null|string, did you maybe forget to handle an error condition?

This check looks for type mismatches where the missing type is false. This is usually indicative of an error condtion.

Consider the follow example

<?php

function getDate($date)
{
    if ($date !== null) {
        return new DateTime($date);
    }

    return false;
}

This function either returns a new DateTime object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returned false before passing on the value to another function or method that may not be able to handle a false.

Loading history...
596
  }
597
598
  /** @noinspection PhpUnusedPrivateMethodInspection */
599
  /**
600
   * @param mixed            $variable
601
   * @param KintVariableData $variableData
602
   */
603
  private static function _parse_unknown(&$variable, KintVariableData $variableData)
604
  {
605
    $type = gettype($variable);
606
    $variableData->type = "UNKNOWN" . (!empty($type) ? " ({$type})" : '');
607
    $variableData->value = var_export($variable, true);
608
  }
609
610
  /**
611
   * @param mixed       $value
612
   * @param null|string $encoding
613
   *
614
   * @return mixed|string
615
   */
616 1
  public static function escape(&$value, $encoding = null)
617
  {
618 1
    if (empty($value)) {
619
      return $value;
620
    }
621
622 1
    $kintEnabled = Kint::enabled();
623
624 1
    if ($kintEnabled === Kint::MODE_CLI) {
625 1
      $value = str_replace("\x1b", "\\x1b", $value);
626
    }
627
628
    if (
629 1
        $kintEnabled === Kint::MODE_CLI
630
        ||
631 1
        $kintEnabled === Kint::MODE_WHITESPACE
632
    ) {
633 1
      return $value;
634
    }
635
636
    if (!$encoding) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $encoding of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null 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...
637
      $encoding = self::_detectEncoding($value);
638
    }
639
    
640
    $value = htmlspecialchars($value, ENT_NOQUOTES, $encoding === 'ASCII' ? 'UTF-8' : $encoding);
641
642
643
    if ($encoding === 'UTF-8') {
644
      // todo we could make the symbols hover-title show the code for the invisible symbol
645
      # when possible force invisible characters to have some sort of display (experimental)
646
      $value = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]/u', '?', $value);
647
    }
648
649
    # this call converts all non-ASCII characters into html chars of format
650
    if (function_exists('mb_encode_numericentity')) {
651
      $value = mb_encode_numericentity(
652
          $value,
653
          array(0x80, 0xffff, 0, 0xffff,),
654
          $encoding
655
      );
656
    }
657
658
    return $value;
659
  }
660
661
  /**
662
   * the only public entry point to return a parsed representation of a variable
663
   *
664
   * @static
665
   *
666
   * @param mixed       $variable
667
   * @param null|string $name
668
   *
669
   * @return KintParser
670
   */
671 1
  public final static function factory(&$variable, $name = null)
0 ignored issues
show
Coding Style introduced by
As per PSR2, final should precede the visibility keyword.
Loading history...
672
  {
673
    # save internal data to revert after dumping to properly handle recursions etc
674
    $revert = array(
675 1
        'level'   => self::$_level,
676 1
        'objects' => self::$_objects,
677
    );
678
679 1
    self::$_level++;
680
681 1
    $varData = new KintVariableData();
682 1
    $varData->name = $name;
683
684
    # first parse the variable based on its type
685 1
    $varType = gettype($variable);
686
687 1
    if ($varType === 'unknown type') {
688
      $varType = 'unknown'; # PHP 5.4 inconsistency
689
    }
690
691 1
    $methodName = '_parse_' . $varType;
692
693
    # objects can be presented in a different way altogether, INSTEAD, not ALONGSIDE the generic parser
694 1
    if ($varType === 'object') {
695 1
      foreach (self::$_objectParsers as $parserClass) {
696 1
        $className = 'kint\parsers\objects\Kint_Objects_' . $parserClass;
697
698
        /** @var $object KintObject */
699 1
        $object = new $className();
700 1
        if (($alternativeTabs = $object->parse($variable)) !== false) {
701
          self::$_skipAlternatives = true;
702
          $alternativeDisplay = new KintVariableData();
703
          $alternativeDisplay->type = $object->name;
704
          $alternativeDisplay->value = $object->value;
705
          $alternativeDisplay->name = $name;
706
707
          foreach ($alternativeTabs as $nameInner => $values) {
708
            $alternative = KintParser::factory($values);
709
            $alternative->type = $nameInner;
0 ignored issues
show
Documentation Bug introduced by
It seems like $nameInner can also be of type integer. However, the property $type is declared as type string. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
710
            if (Kint::enabled() === Kint::MODE_RICH) {
711
              empty($alternative->value) and $alternative->value = $alternative->extendedValue;
0 ignored issues
show
Documentation Bug introduced by
It seems like $alternative->extendedValue of type array<integer,object<kint\inc\KintVariableData>> is incompatible with the declared type string of property $value.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
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...
712
              $alternativeDisplay->_alternatives[] = $alternative;
713
            } else {
714
              $alternativeDisplay->extendedValue[] = $alternative;
715
            }
716
          }
717
718
          self::$_skipAlternatives = false;
719
          self::$_level = $revert['level'];
720
          self::$_objects = $revert['objects'];
721
722 1
          return $alternativeDisplay;
723
        }
724
      }
725
    }
726
727
    # base type parser returning false means "stop processing further": e.g. recursion
728 1
    if (self::$methodName($variable, $varData) === false) {
729
      self::$_level--;
730
731
      return $varData;
732
    }
733
734
    if (
735 1
        !self::$_skipAlternatives
736
        &&
737 1
        Kint::enabled() === Kint::MODE_RICH
738
    ) {
739
      # if an alternative returns something that can be represented in an alternative way, don't :)
740
      self::$_skipAlternatives = true;
741
742
      # now check whether the variable can be represented in a different way
743
      foreach (self::$_customDataTypes as $parserClass) {
744
        $className = 'kint\parsers\custom\Kint_Parsers_' . $parserClass;
745
746
        /** @var $parser KintParser */
747
748
        $parser = new $className();
749
        $parser->name = $name; # the parser may overwrite the name value, so set it first
750
751
        if ($parser->_parse($variable) !== false) {
752
          $varData->_alternatives[] = $parser;
753
        }
754
      }
755
756
      # if alternatives exist, push extendedValue to their front and display it as one of alternatives
757
      if (!empty($varData->_alternatives) && isset($varData->extendedValue)) {
758
        $_ = new KintVariableData();
759
760
        $_->value = $varData->extendedValue;
0 ignored issues
show
Documentation Bug introduced by
It seems like $varData->extendedValue of type array<integer,object<kint\inc\KintVariableData>> is incompatible with the declared type string of property $value.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
761
        $_->type = 'contents';
762
        $_->size = null;
763
764
        array_unshift($varData->_alternatives, $_);
765
        $varData->extendedValue = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like null of type null is incompatible with the declared type array<integer,object<kint\inc\KintVariableData>> of property $extendedValue.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
766
      }
767
768
      self::$_skipAlternatives = false;
769
    }
770
771 1
    self::$_level = $revert['level'];
772 1
    self::$_objects = $revert['objects'];
773
774 1
    if (strlen($varData->name) > 80) {
775
      $varData->name =
776
          self::_substr($varData->name, 0, 37)
777
          . '...'
778
          . self::_substr($varData->name, -38, null);
779
    }
780
781 1
    return $varData;
782
  }
783
784
  /**
785
   * reset
786
   */
787 1
  public static function reset()
788
  {
789 1
    self::$_level = 0;
790 1
    self::$_objects = self::$_marker = null;
0 ignored issues
show
Documentation Bug introduced by
It seems like self::$_marker = null of type null is incompatible with the declared type array of property $_objects.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
791 1
  }
792
793
}
794