Issues (73)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

src/kint/inc/KintParser.php (18 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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