Completed
Push — master ( 1e94ba...5d5002 )
by Lars
15:21
created

KintParser   D

Complexity

Total Complexity 112

Size/Duplication

Total Lines 773
Duplicated Lines 1.16 %

Coupling/Cohesion

Components 1
Dependencies 5

Test Coverage

Coverage 40.63%

Importance

Changes 5
Bugs 2 Features 0
Metric Value
c 5
b 2
f 0
dl 9
loc 773
ccs 156
cts 384
cp 0.4063
rs 4.4444
wmc 112
lcom 1
cbo 5

16 Methods

Rating   Name   Duplication   Size   Complexity  
A _checkDepth() 0 4 2
_parse() 0 1 ?
A _parse_boolean() 0 5 2
A _parse_double() 0 6 1
A _parse_integer() 0 6 1
A _parse_null() 0 5 1
B _parse_resource() 0 26 6
A _parse_unknown() 0 6 2
C _decorateCell() 0 35 7
D _parse_array() 0 152 28
F _parse_object() 9 132 20
C _isArrayTabular() 0 37 11
C _parse_string() 0 43 7
C escape() 0 35 7
D factory() 0 112 16
A reset() 0 5 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like KintParser 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 KintParser, and based on these observations, apply Extract Interface, too.

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 2
  private static function _checkDepth()
73
  {
74 2
    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 2
  private static function _isArrayTabular(array &$variable)
124
  {
125 2
    if (Kint::enabled() !== Kint::MODE_RICH) {
126 1
      return false;
127
    }
128
129 1
    $arrayKeys = array();
130 1
    $keys = null;
131 1
    $closeEnough = false;
132 1
    foreach ($variable as $row) {
133 1
      if (!is_array($row) || empty($row)) {
134 1
        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 2
  private static function _parse_array(&$variable, KintVariableData $variableData)
178
  {
179 2
    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 2
    $globalsDetector = false;
183
    if (
184 2
        isset($variable['GLOBALS'])
185 2
        &&
186
        is_array($variable['GLOBALS'])
187 2
    ) {
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 2
    $variableData->type = 'array';
201 2
    $variableData->size = count($variable);
202
203 2
    if ($variableData->size === 0) {
204
      /** @noinspection PhpInconsistentReturnPointsInspection */
205
      return;
206
    }
207
208 2
    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 2
    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 2
    $isSequential = self::_isSequential($variable);
225
226
    if (
227 2
        $variableData->size > 1
228 2
        &&
229 2
        ($arrayKeys = self::_isArrayTabular($variable)) !== false
230 2
    ) {
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(self::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 = self::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 2
      $variable[self::$_marker] = true;
299 2
      $extendedValue = array();
300
301 2
      foreach ($variable as $key => &$val) {
302 2
        if ($key === self::$_marker) {
303 2
          continue;
304
        }
305
306 2
        $output = self::factory($val);
307 2
        if ($output->value === self::$_marker) {
308
          $variableData->value = '*RECURSION*'; // recursion occurred on a higher level, thus $this is recursion
309
          return false;
310
        }
311 2
        if (!$isSequential) {
312
          $output->operator = '=>';
313
        }
314 2
        $output->name = $isSequential ? null : "'" . $key . "'";
315 2
        $extendedValue[] = $output;
316 2
      }
317 2
      unset($val);
318 2
      $variableData->extendedValue = $extendedValue;
319
    }
320
321 2
    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 2
    unset($variable[self::$_marker]);
326
327 2
    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 2
  private static function _parse_double(&$variable, KintVariableData $variableData)
347
  {
348 2
    $variableData->type = 'float';
349
    /** @noinspection ReferenceMismatchInspection */
350 2
    $variableData->value = $variable;
351 2
  }
352
353
  /** @noinspection PhpUnusedPrivateMethodInspection */
354
  /**
355
   * @param mixed            $variable
356
   * @param KintVariableData $variableData
357
   */
358 2
  private static function _parse_integer(&$variable, KintVariableData $variableData)
359
  {
360 2
    $variableData->type = 'integer';
361
    /** @noinspection ReferenceMismatchInspection */
362 2
    $variableData->value = $variable;
363 2
  }
364
365
366
  /** @noinspection PhpUnusedPrivateMethodInspection */
367
  /**
368
   * @param mixed            $variable
369
   * @param KintVariableData $variableData
370
   */
371 2
  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 2
    $variableData->type = 'NULL';
375 2
  }
376
377
  /** @noinspection PhpUnusedPrivateMethodInspection */
378
  /**
379
   * @param mixed            $variable
380
   * @param KintVariableData $variableData
381
   *
382
   * @return bool
383
   */
384 2
  private static function _parse_object(&$variable, KintVariableData $variableData)
385
  {
386 2
    if (function_exists('spl_object_hash')) {
387 2
      $hash = spl_object_hash($variable);
388 2
    } 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 2
    $castedArray = (array)$variable;
398 2
    $variableData->type = get_class($variable);
399 2
    $variableData->size = count($castedArray);
400
401 2
    if (isset(self::$_objects[$hash])) {
402
      $variableData->value = '*RECURSION*';
403
404
      return false;
405
    }
406 2
    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 2
        $variableData->type === 'ArrayObject'
418 2
        ||
419 2
        is_subclass_of($variable, 'ArrayObject')
420 2
    ) {
421
422
      /* @var $variable \ArrayObject */
423
424
      $arrayObjectFlags = $variable->getFlags();
425
      $variable->setFlags(\ArrayObject::STD_PROP_LIST);
426
    }
427
428 2
    self::$_objects[$hash] = true; // TODO: store reflectorObject here for alternatives cache
429 2
    $reflector = new \ReflectionObject($variable);
430
431
    # add link to definition of user-land objects
432
    if (
433
        Kint::$fileLinkFormat
434 2
        &&
435
        Kint::enabled() === Kint::MODE_RICH
436 2
        &&
437
        $reflector->isUserDefined()
438 2
    ) {
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 2
    $variableData->size = 0;
445
446 2
    $extendedValue = array();
447 2
    $encountered = array();
448
449
    # copy the object as an array as it provides more info than Reflection (depends)
450 2
    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 = self::factory($value, self::escape($key));
471
      $output->access = $access;
472
      $output->operator = '->';
473
      $extendedValue[] = $output;
474
      $variableData->size++;
475 2
    }
476
477 2
    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 = self::factory($value, self::escape($name));
500
      $output->access = $access;
501
      $output->operator = '->';
502
      $extendedValue[] = $output;
503
      $variableData->size++;
504 2
    }
505
506 2
    if (isset($arrayObjectFlags)) {
507
      $variable->setFlags($arrayObjectFlags);
508
    }
509
510 2
    if ($variableData->size) {
511
      $variableData->extendedValue = $extendedValue;
512
    }
513
514 2
    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 2
  private static function _parse_string(&$variable, KintVariableData $variableData)
555
  {
556 2
    $variableData->type = 'string';
557
558 2
    $encoding = self::_detectEncoding($variable);
559 2
    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 2
      $variableData->type .= ' [' . $encoding . ']';
561 2
    }
562
563 2
    $variableData->size = UTF8::strlen($variable);
564 2
    if (Kint::enabled() !== Kint::MODE_RICH) {
565 1
      $variableData->value = '"' . self::escape($variable, $encoding) . '"';
566
567 1
      return;
568
    }
569
570 1
    if (!self::$_placeFullStringInValue) {
571
572 1
      $strippedString = preg_replace('[\s+]', ' ', $variable);
573
      if (
574
          Kint::$maxStrLength
575 1
          &&
576
          $variableData->size > Kint::$maxStrLength
577 1
      ) {
578
579
        $tmpStrippedString = self::_substr($strippedString, 0, Kint::$maxStrLength, $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\KintVariableData::_substr() does only seem to accept string|null, 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...
580
581
        // encode and truncate
582
        $variableData->value = '"' . self::escape($tmpStrippedString, $encoding) . '&hellip;"';
583
        $variableData->extendedValue = self::escape($variable, $encoding);
0 ignored issues
show
Documentation Bug introduced by
It seems like self::escape($variable, $encoding) of type string or 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...
584
585
        return;
586 1
      } elseif ($variable !== $strippedString) { // omit no data from display
587
588
        $variableData->value = '"' . self::escape($variable, $encoding) . '"';
589
        $variableData->extendedValue = self::escape($variable, $encoding);
590
591
        return;
592
      }
593 1
    }
594
595 1
    $variableData->value = '"' . self::escape($variable, $encoding) . '"';
596 1
  }
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|false|string $encoding
613
   *
614
   * @return string|null
615
   */
616 2
  public static function escape(&$value, $encoding = null)
617
  {
618 2
    if (empty($value)) {
619
      return $value;
620
    }
621
622 2
    $kintEnabled = Kint::enabled();
623
624 2
    if ($kintEnabled === Kint::MODE_CLI) {
625 1
      $value = str_replace("\x1b", '\\x1b', $value);
626 1
    }
627
628
    if (
629
        $kintEnabled === Kint::MODE_CLI
630 2
        ||
631
        $kintEnabled === Kint::MODE_WHITESPACE
632 2
    ) {
633 1
      return $value;
634
    }
635
636 1
    if (!$encoding) {
637
      $encoding = self::_detectEncoding($value);
638
    }
639
640 1
    $value = htmlspecialchars($value, ENT_NOQUOTES, $encoding === 'ASCII' ? 'UTF-8' : $encoding);
641
642
    // TODO: we could make the symbols hover-title show the code for the invisible symbol
643 1
    # when possible force invisible characters to have some sort of display (experimental)
644
    $value = UTF8::remove_invisible_characters($value, '?');
0 ignored issues
show
Documentation introduced by
'?' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
645
646 1
    # this call converts all non-ASCII characters into html chars of format
647 1
    $value = UTF8::html_encode($value, true, $encoding);
0 ignored issues
show
Security Bug introduced by
It seems like $encoding can also be of type false; however, voku\helper\UTF8::html_encode() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
648
649
    return $value;
650 1
  }
651 1
652 1
  /**
653 1
   * the only public entry point to return a parsed representation of a variable
654
   *
655 1
   * @static
656 1
   *
657
   * @param mixed       $variable
658 1
   * @param null|string $name
659
   *
660
   * @return KintParser
661
   */
662
  final public static function factory(&$variable, $name = null)
663
  {
664
    # save internal data to revert after dumping to properly handle recursions etc
665
    $revert = array(
666
        'level'   => self::$_level,
667
        'objects' => self::$_objects,
668
    );
669
670
    self::$_level++;
671 2
672
    $varData = new KintVariableData();
673
    $varData->name = $name;
674
675 2
    # first parse the variable based on its type
676 2
    $varType = gettype($variable);
677 2
678
    if ($varType === 'unknown type') {
679 2
      $varType = 'unknown'; # PHP 5.4 inconsistency
680
    }
681 2
682 2
    $methodName = '_parse_' . $varType;
683
684
    # objects can be presented in a different way altogether, INSTEAD, not ALONGSIDE the generic parser
685 2
    if ($varType === 'object') {
686
      foreach (self::$_objectParsers as $parserClass) {
687 2
        $className = 'kint\parsers\objects\Kint_Objects_' . $parserClass;
688
689
        /** @var $object KintObject */
690
        $object = new $className();
691 2
        if (($alternativeTabs = $object->parse($variable)) !== false) {
692
          self::$_skipAlternatives = true;
693
          $alternativeDisplay = new KintVariableData();
694 2
          $alternativeDisplay->type = $object->name;
695 2
          $alternativeDisplay->value = $object->value;
696 2
          $alternativeDisplay->name = $name;
697
698
          foreach ($alternativeTabs as $nameInner => $values) {
699 2
            $alternative = self::factory($values);
700 2
            $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...
701
            if (Kint::enabled() === Kint::MODE_RICH) {
702
              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...
703
              $alternativeDisplay->_alternatives[] = $alternative;
704
            } else {
705
              $alternativeDisplay->extendedValue[] = $alternative;
706
            }
707
          }
708
709
          self::$_skipAlternatives = false;
710
          self::$_level = $revert['level'];
711
          self::$_objects = $revert['objects'];
712
713
          return $alternativeDisplay;
714
        }
715
      }
716
    }
717
718
    # base type parser returning false means "stop processing further": e.g. recursion
719
    if (self::$methodName($variable, $varData) === false) {
720
      self::$_level--;
721
722
      return $varData;
723
    }
724 2
725 2
    if (
726
        !self::$_skipAlternatives
727
        &&
728 2
        Kint::enabled() === Kint::MODE_RICH
729
    ) {
730
      # if an alternative returns something that can be represented in an alternative way, don't :)
731
      self::$_skipAlternatives = true;
732
733
      # now check whether the variable can be represented in a different way
734
      foreach (self::$_customDataTypes as $parserClass) {
735
        $className = 'kint\parsers\custom\Kint_Parsers_' . $parserClass;
736 2
737 2
        /** @var $parser KintParser */
738 2
739
        $parser = new $className();
740 1
        $parser->name = $name; # the parser may overwrite the name value, so set it first
741
742
        if ($parser->_parse($variable) !== false) {
743 1
          $varData->_alternatives[] = $parser;
744 1
        }
745
      }
746
747
      # if alternatives exist, push extendedValue to their front and display it as one of alternatives
748 1
      if (!empty($varData->_alternatives) && isset($varData->extendedValue)) {
749 1
        $_ = new KintVariableData();
750
751 1
        $_->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...
752
        $_->type = 'contents';
753
        $_->size = null;
754 1
755
        array_unshift($varData->_alternatives, $_);
756
        $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...
757 1
      }
758
759
      self::$_skipAlternatives = false;
760
    }
761
762
    self::$_level = $revert['level'];
763
    self::$_objects = $revert['objects'];
764
765
    if (strlen($varData->name) > 80) {
766
      $varData->name =
767
          self::_substr($varData->name, 0, 37)
768 1
          . '...'
769 1
          . self::_substr($varData->name, -38, null);
770
    }
771 2
772 2
    return $varData;
773
  }
774 2
775
  /**
776
   * reset
777
   */
778
  public static function reset()
779
  {
780
    self::$_level = 0;
781 2
    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...
782
  }
783
784
}
785