Completed
Pull Request — master (#133)
by Goffy
16:45
created

kintParser::_parse_integer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
c 1
b 1
f 0
dl 0
loc 5
rs 9.4285
cc 1
eloc 3
nc 1
nop 2
1
<?php
2
3
abstract class kintParser extends kintVariableData
4
{
5
	private static $_level = 0;
6
	private static $_customDataTypes;
7
	private static $_objectParsers;
8
	private static $_objects;
9
	private static $_marker;
10
11
	private static $_skipAlternatives = false;
12
13
	private static $_placeFullStringInValue = false;
14
15
16
	private static function _init()
17
	{
18
		$fh = opendir( KINT_DIR . 'parsers/custom/' );
19 View Code Duplication
		while ( $fileName = readdir( $fh ) ) {
20
			if ( substr( $fileName, -4 ) !== '.php' ) continue;
21
22
			require KINT_DIR . 'parsers/custom/' . $fileName;
23
			self::$_customDataTypes[] = substr( $fileName, 0, -4 );
24
		}
25
		$fh = opendir( KINT_DIR . 'parsers/objects/' );
26 View Code Duplication
		while ( $fileName = readdir( $fh ) ) {
27
			if ( substr( $fileName, -4 ) !== '.php' ) continue;
28
29
			require KINT_DIR . 'parsers/objects/' . $fileName;
30
			self::$_objectParsers[] = substr( $fileName, 0, -4 );
31
		}
32
	}
33
34
	public static function reset()
35
	{
36
		self::$_level   = 0;
37
		self::$_objects = self::$_marker = null;
38
	}
39
40
	/**
41
	 * main and usually single method a custom parser must implement
42
	 *
43
	 * @param mixed $variable
44
	 *
45
	 * @return mixed [!!!] false is returned if the variable is not of current type
46
	 */
47
	abstract protected function _parse( & $variable );
48
49
50
	/**
51
	 * the only public entry point to return a parsed representation of a variable
52
	 *
53
	 * @static
54
	 *
55
	 * @param      $variable
56
	 * @param null $name
57
	 *
58
	 * @throws Exception
59
	 * @return \kintParser
60
	 */
61
	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...
62
	{
63
		isset( self::$_customDataTypes ) or self::_init();
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...
64
65
		# save internal data to revert after dumping to properly handle recursions etc
66
		$revert = array(
67
			'level'   => self::$_level,
68
			'objects' => self::$_objects,
69
		);
70
71
		self::$_level++;
72
73
		$varData       = new kintVariableData;
74
		$varData->name = $name;
75
76
		# first parse the variable based on its type
77
		$varType = gettype( $variable );
78
		$varType === 'unknown type' and $varType = 'unknown'; # PHP 5.4 inconsistency
0 ignored issues
show
Comprehensibility Best Practice introduced by
Using logical operators such as and instead of && is generally not recommended.

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

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

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

Let’s take a look at a few examples:

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

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


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

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

Logical Operators are used for Control-Flow

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

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

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

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

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

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

Loading history...
79
		$methodName = '_parse_' . $varType;
80
81
		# objects can be presented in a different way altogether, INSTEAD, not ALONGSIDE the generic parser
82
		if ( $varType === 'object' ) {
83
			foreach ( self::$_objectParsers as $parserClass ) {
84
				$className = 'Kint_Objects_' . $parserClass;
85
86
				/** @var $object KintObject */
87
				$object = new $className;
88
				if ( ( $alternativeTabs = $object->parse( $variable ) ) !== false ) {
89
					self::$_skipAlternatives   = true;
90
					$alternativeDisplay        = new kintVariableData;
91
					$alternativeDisplay->type  = $object->name;
92
					$alternativeDisplay->value = $object->value;
93
					$alternativeDisplay->name  = $name;
94
95
					foreach ( $alternativeTabs as $name => $values ) {
96
						$alternative       = kintParser::factory( $values );
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
97
						$alternative->type = $name;
0 ignored issues
show
Documentation Bug introduced by
It seems like $name 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...
98
						if ( Kint::enabled() === Kint::MODE_RICH ) {
99
							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<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...
100
							$alternativeDisplay->_alternatives[] = $alternative;
101
						} else {
102
							$alternativeDisplay->extendedValue[] = $alternative;
103
						}
104
					}
105
106
					self::$_skipAlternatives = false;
107
					self::$_level   = $revert['level'];
108
					self::$_objects = $revert['objects'];
109
					return $alternativeDisplay;
110
				}
111
			}
112
		}
113
114
		# base type parser returning false means "stop processing further": e.g. recursion
115
		if ( self::$methodName( $variable, $varData ) === false ) {
116
			self::$_level--;
117
			return $varData;
118
		}
119
120
		if ( Kint::enabled() === Kint::MODE_RICH && !self::$_skipAlternatives ) {
121
			# if an alternative returns something that can be represented in an alternative way, don't :)
122
			self::$_skipAlternatives = true;
123
124
			# now check whether the variable can be represented in a different way
125
			foreach ( self::$_customDataTypes as $parserClass ) {
0 ignored issues
show
Bug introduced by
The expression self::$_customDataTypes of type null|array<integer,string> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
126
				$className = 'Kint_Parsers_' . $parserClass;
127
128
				/** @var $parser kintParser */
129
				$parser       = new $className;
130
				$parser->name = $name; # the parser may overwrite the name value, so set it first
131
132
				if ( $parser->_parse( $variable ) !== false ) {
133
					$varData->_alternatives[] = $parser;
134
				}
135
			}
136
137
138
			# if alternatives exist, push extendedValue to their front and display it as one of alternatives
139
			if ( !empty( $varData->_alternatives ) && isset( $varData->extendedValue ) ) {
140
				$_ = new kintVariableData;
141
142
				$_->value = $varData->extendedValue;
0 ignored issues
show
Documentation Bug introduced by
It seems like $varData->extendedValue of type array<integer,object<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...
143
				$_->type  = 'contents';
144
				$_->size  = null;
145
146
				array_unshift( $varData->_alternatives, $_ );
147
				$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<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...
148
			}
149
150
			self::$_skipAlternatives = false;
151
		}
152
153
		self::$_level   = $revert['level'];
154
		self::$_objects = $revert['objects'];
155
156
		if ( strlen( $varData->name ) > 80 ) {
157
			$varData->name =
158
				self::_substr( $varData->name, 0, 37 )
159
				. '...'
160
				. self::_substr( $varData->name, -38, null );
161
		}
162
		return $varData;
163
	}
164
165
	private static function _checkDepth()
166
	{
167
		return Kint::$maxLevels != 0 && self::$_level >= Kint::$maxLevels;
168
	}
169
170
	private static function _isArrayTabular( array $variable )
171
	{
172
		if ( Kint::enabled() !== Kint::MODE_RICH ) return false;
173
174
		$arrayKeys   = array();
175
		$keys        = null;
176
		$closeEnough = false;
177
		foreach ( $variable as $row ) {
178
			if ( !is_array( $row ) || empty( $row ) ) return false;
179
180
			foreach ( $row as $col ) {
181
				if ( !empty( $col ) && !is_scalar( $col ) ) return false; // todo add tabular "tolerance"
182
			}
183
184
			if ( isset( $keys ) && !$closeEnough ) {
185
				# let's just see if the first two rows have same keys, that's faster and has the
186
				# positive side effect of easily spotting missing keys in later rows
187
				if ( $keys !== array_keys( $row ) ) return false;
188
189
				$closeEnough = true;
190
			} else {
191
				$keys = array_keys( $row );
192
			}
193
194
			$arrayKeys = array_unique( array_merge( $arrayKeys, $keys ) );
195
		}
196
197
		return $arrayKeys;
198
	}
199
200
	private static function _decorateCell( kintVariableData $kintVar )
201
	{
202
		if ( $kintVar->extendedValue !== null || !empty( $kintVar->_alternatives ) ) {
203
			return '<td>' . Kint_Decorators_Rich::decorate( $kintVar ) . '</td>';
204
		}
205
206
		$output = '<td';
207
208
		if ( $kintVar->value !== null ) {
209
			$output .= ' title="' . $kintVar->type;
210
211
			if ( $kintVar->size !== null ) {
212
				$output .= " (" . $kintVar->size . ")";
213
			}
214
215
			$output .= '">' . $kintVar->value;
216
		} else {
217
			$output .= '>';
218
219
			if ( $kintVar->type !== 'NULL' ) {
220
				$output .= '<u>' . $kintVar->type;
221
222
				if ( $kintVar->size !== null ) {
223
					$output .= "(" . $kintVar->size . ")";
224
				}
225
226
				$output .= '</u>';
227
			} else {
228
				$output .= '<u>NULL</u>';
229
			}
230
		}
231
232
233
		return $output . '</td>';
234
	}
235
236
237
	public static function escape( $value, $encoding = null )
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
238
	{
239
		if ( empty( $value ) ) return $value;
240
241
		if ( Kint::enabled() === Kint::MODE_CLI ) {
242
			$value = str_replace( "\x1b", "\\x1b", $value );
243
		}
244
245
		if ( Kint::enabled() === Kint::MODE_CLI || Kint::enabled() === Kint::MODE_WHITESPACE ) return $value;
246
247
		$encoding or $encoding = self::_detectEncoding( $value );
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...
248
		$value = htmlspecialchars( $value, ENT_NOQUOTES, $encoding === 'ASCII' ? 'UTF-8' : $encoding );
249
250
251
		if ( $encoding === 'UTF-8' ) {
252
			// todo we could make the symbols hover-title show the code for the invisible symbol
253
			# when possible force invisible characters to have some sort of display (experimental)
254
			$value = preg_replace( '/[\x00-\x08\x0B\x0C\x0E-\x1F\x80-\x9F]/u', '?', $value );
255
		}
256
257
		# this call converts all non-ASCII characters into html chars of format
258
		if ( function_exists( 'mb_encode_numericentity' ) ) {
259
			$value = mb_encode_numericentity(
260
				$value,
261
				array( 0x80, 0xffff, 0, 0xffff, ),
262
				$encoding
263
			);
264
		}
265
266
		return $value;
267
	}
268
269
270
	private static $_dealingWithGlobals = false;
271
272
	private static function _parse_array( &$variable, kintVariableData $variableData )
273
	{
274
		isset( self::$_marker ) or self::$_marker = "\x00" . uniqid();
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...
275
276
		# naturally, $GLOBALS variable is an intertwined recursion nightmare, use black magic
277
		$globalsDetector = false;
278
		if ( array_key_exists( 'GLOBALS', $variable ) && is_array( $variable['GLOBALS'] ) ) {
279
			$globalsDetector = "\x01" . uniqid();
280
281
			$variable['GLOBALS'][ $globalsDetector ] = true;
282
			if ( isset( $variable[ $globalsDetector ] ) ) {
283
				unset( $variable[ $globalsDetector ] );
284
				self::$_dealingWithGlobals = true;
285
			} else {
286
				unset( $variable['GLOBALS'][ $globalsDetector ] );
287
				$globalsDetector = false;
288
			}
289
		}
290
291
		$variableData->type = 'array';
292
		$variableData->size = count( $variable );
293
294
		if ( $variableData->size === 0 ) {
295
			return;
296
		}
297
		if ( isset( $variable[ self::$_marker ] ) ) { # recursion; todo mayhaps show from where
298
			if ( self::$_dealingWithGlobals ) {
299
				$variableData->value = '*RECURSION*';
300
			} else {
301
				unset( $variable[ self::$_marker ] );
302
				$variableData->value = self::$_marker;
303
			}
304
			return false;
305
		}
306
		if ( self::_checkDepth() ) {
307
			$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<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...
308
			return false;
309
		}
310
311
		$isSequential = self::_isSequential( $variable );
312
313
		if ( $variableData->size > 1 && ( $arrayKeys = self::_isArrayTabular( $variable ) ) !== false ) {
314
			$variable[ self::$_marker ] = true; # this must be AFTER _isArrayTabular
315
			$firstRow                   = true;
316
			$extendedValue              = '<table class="kint-report"><thead>';
317
318
			foreach ( $variable as $rowIndex => & $row ) {
319
				# display strings in their full length
320
				self::$_placeFullStringInValue = true;
321
322
				if ( $rowIndex === self::$_marker ) continue;
323
324
				if ( isset( $row[ self::$_marker ] ) ) {
325
					$variableData->value = "*RECURSION*";
326
					return false;
327
				}
328
329
330
				$extendedValue .= '<tr>';
331
				if ( $isSequential ) {
332
					$output = '<td>' . '#' . ( $rowIndex + 1 )  . '</td>';
333
				} else {
334
					$output = self::_decorateCell( kintParser::factory( $rowIndex ) );
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
335
				}
336
				if ( $firstRow ) {
337
					$extendedValue .= '<th>&nbsp;</th>';
338
				}
339
340
				# we iterate the known full set of keys from all rows in case some appeared at later rows,
341
				# as we only check the first two to assume
342
				foreach ( $arrayKeys as $key ) {
343
					if ( $firstRow ) {
344
						$extendedValue .= '<th>' . self::escape( $key ) . '</th>';
345
					}
346
347
					if ( !array_key_exists( $key, $row ) ) {
348
						$output .= '<td class="kint-empty"></td>';
349
						continue;
350
					}
351
352
					$var = kintParser::factory( $row[ $key ] );
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
353
354
					if ( $var->value === self::$_marker ) {
355
						$variableData->value = '*RECURSION*';
356
						return false;
357
					} elseif ( $var->value === '*RECURSION*' ) {
358
						$output .= '<td class="kint-empty"><u>*RECURSION*</u></td>';
359
					} else {
360
						$output .= self::_decorateCell( $var );
361
					}
362
					unset( $var );
363
				}
364
365
				if ( $firstRow ) {
366
					$extendedValue .= '</tr></thead><tr>';
367
					$firstRow = false;
368
				}
369
370
				$extendedValue .= $output . '</tr>';
371
			}
372
			self::$_placeFullStringInValue = false;
373
374
			$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<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...
375
376
		} else {
377
			$variable[ self::$_marker ] = true;
378
			$extendedValue              = array();
379
380
			foreach ( $variable as $key => & $val ) {
381
				if ( $key === self::$_marker ) continue;
382
383
				$output = kintParser::factory( $val );
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
384
				if ( $output->value === self::$_marker ) {
385
					$variableData->value = "*RECURSION*"; // recursion occurred on a higher level, thus $this is recursion
386
					return false;
387
				}
388
				if ( !$isSequential ) {
389
					$output->operator = '=>';
390
				}
391
				$output->name    = $isSequential ? null : "'" . $key . "'";
392
				$extendedValue[] = $output;
393
			}
394
			$variableData->extendedValue = $extendedValue;
395
		}
396
397
		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...
398
			self::$_dealingWithGlobals = false;
399
		}
400
401
		unset( $variable[ self::$_marker ] );
402
	}
403
404
405
	private static function _parse_object( &$variable, kintVariableData $variableData )
406
	{
407
		if ( function_exists( 'spl_object_hash' ) ) {
408
			$hash = spl_object_hash( $variable );
409
		} else {
410
			ob_start();
411
			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...
412
			preg_match( '[#(\d+)]', ob_get_clean(), $match );
413
			$hash = $match[1];
414
		}
415
416
		$castedArray        = (array) $variable;
417
		$variableData->type = get_class( $variable );
418
		$variableData->size = count( $castedArray );
419
420
		if ( isset( self::$_objects[ $hash ] ) ) {
421
			$variableData->value = '*RECURSION*';
422
			return false;
423
		}
424
		if ( self::_checkDepth() ) {
425
			$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<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...
426
			return false;
427
		}
428
429
430
		# ArrayObject (and maybe ArrayIterator, did not try yet) unsurprisingly consist of mainly dark magic.
431
		# What bothers me most, var_dump sees no problem with it, and ArrayObject also uses a custom,
432
		# undocumented serialize function, so you can see the properties in internal functions, but
433
		# can never iterate some of them if the flags are not STD_PROP_LIST. Fun stuff.
434
		if ( $variableData->type === 'ArrayObject' || is_subclass_of( $variable, 'ArrayObject' ) ) {
435
			$arrayObjectFlags = $variable->getFlags();
436
			$variable->setFlags( ArrayObject::STD_PROP_LIST );
437
		}
438
439
		self::$_objects[ $hash ] = true; // todo store reflectorObject here for alternatives cache
440
		$reflector               = new ReflectionObject( $variable );
441
442
		# add link to definition of userland objects
443
		if ( Kint::enabled() === Kint::MODE_RICH && Kint::$fileLinkFormat && $reflector->isUserDefined() ) {
444
			$url = Kint::getIdeLink( $reflector->getFileName(), $reflector->getStartLine() );
445
446
			$class              = ( strpos( $url, 'http://' ) === 0 ) ? 'class="kint-ide-link" ' : '';
447
			$variableData->type = "<a {$class}href=\"{$url}\">{$variableData->type}</a>";
448
		}
449
		$variableData->size = 0;
450
451
		$extendedValue = array();
452
		$encountered   = array();
453
454
		# copy the object as an array as it provides more info than Reflection (depends)
455
		foreach ( $castedArray as $key => $value ) {
456
			/* casting object to array:
457
			 * integer properties are inaccessible;
458
			 * private variables have the class name prepended to the variable name;
459
			 * protected variables have a '*' prepended to the variable name.
460
			 * These prepended values have null bytes on either side.
461
			 * http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
462
			 */
463
			if ( $key{0} === "\x00" ) {
464
465
				$access = $key{1} === "*" ? "protected" : "private";
466
467
				// Remove the access level from the variable name
468
				$key = substr( $key, strrpos( $key, "\x00" ) + 1 );
469
			} else {
470
				$access = "public";
471
			}
472
473
			$encountered[ $key ] = true;
474
475
			$output           = kintParser::factory( $value, self::escape( $key ) );
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
476
			$output->access   = $access;
477
			$output->operator = '->';
478
			$extendedValue[]  = $output;
479
			$variableData->size++;
480
		}
481
482
		foreach ( $reflector->getProperties() as $property ) {
483
			$name = $property->name;
484
			if ( $property->isStatic() || isset( $encountered[ $name ] ) ) continue;
485
486 View Code Duplication
			if ( $property->isProtected() ) {
487
				$property->setAccessible( true );
488
				$access = "protected";
489
			} elseif ( $property->isPrivate() ) {
490
				$property->setAccessible( true );
491
				$access = "private";
492
			} else {
493
				$access = "public";
494
			}
495
496
			$value = $property->getValue( $variable );
497
498
			$output           = kintParser::factory( $value, self::escape( $name ) );
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
499
			$output->access   = $access;
500
			$output->operator = '->';
501
			$extendedValue[]  = $output;
502
			$variableData->size++;
503
		}
504
505
		if ( isset( $arrayObjectFlags ) ) {
506
			$variable->setFlags( $arrayObjectFlags );
507
		}
508
509
		if ( $variableData->size ) {
510
			$variableData->extendedValue = $extendedValue;
511
		}
512
	}
513
514
515
	private static function _parse_boolean( &$variable, kintVariableData $variableData )
516
	{
517
		$variableData->type  = 'bool';
518
		$variableData->value = $variable ? 'TRUE' : 'FALSE';
519
	}
520
521
	private static function _parse_double( &$variable, kintVariableData $variableData )
522
	{
523
		$variableData->type  = 'float';
524
		$variableData->value = $variable;
525
	}
526
527
	private static function _parse_integer( &$variable, kintVariableData $variableData )
528
	{
529
		$variableData->type  = 'integer';
530
		$variableData->value = $variable;
531
	}
532
533
	private static function _parse_null( &$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...
534
	{
535
		$variableData->type = 'NULL';
536
	}
537
538
	private static function _parse_resource( &$variable, kintVariableData $variableData )
539
	{
540
		$resourceType       = get_resource_type( $variable );
541
		$variableData->type = "resource ({$resourceType})";
542
543
		if ( $resourceType === 'stream' && $meta = stream_get_meta_data( $variable ) ) {
544
545
			if ( isset( $meta['uri'] ) ) {
546
				$file = $meta['uri'];
547
548
				if ( function_exists( 'stream_is_local' ) ) {
549
					// Only exists on PHP >= 5.2.4
550
					if ( stream_is_local( $file ) ) {
551
						$file = Kint::shortenPath( $file );
552
					}
553
				}
554
555
				$variableData->value = $file;
556
			}
557
		}
558
	}
559
560
	private static function _parse_string( &$variable, kintVariableData $variableData )
561
	{
562
		$variableData->type = 'string';
563
564
		$encoding = self::_detectEncoding( $variable );
565
		if ( $encoding !== 'ASCII' ) {
566
			$variableData->type .= ' ' . $encoding;
567
		}
568
569
570
		$variableData->size = self::_strlen( $variable, $encoding );
571
		if ( Kint::enabled() !== Kint::MODE_RICH ) {
572
			$variableData->value = '"' . self::escape( $variable, $encoding ) . '"';
573
			return;
574
		}
575
576
577
		if ( !self::$_placeFullStringInValue ) {
578
579
			$strippedString = preg_replace( '[\s+]', ' ', $variable );
580
			if ( Kint::$maxStrLength && $variableData->size > Kint::$maxStrLength ) {
581
582
				// encode and truncate
583
				$variableData->value         = '"'
584
					. self::escape( self::_substr( $strippedString, 0, Kint::$maxStrLength, $encoding ), $encoding )
585
					. '&hellip;"';
586
				$variableData->extendedValue = self::escape( $variable, $encoding );
587
588
				return;
589
			} elseif ( $variable !== $strippedString ) { // omit no data from display
590
591
				$variableData->value         = '"' . self::escape( $variable, $encoding ) . '"';
592
				$variableData->extendedValue = self::escape( $variable, $encoding );
593
594
				return;
595
			}
596
		}
597
598
		$variableData->value = '"' . self::escape( $variable, $encoding ) . '"';
599
	}
600
601
	private static function _parse_unknown( &$variable, kintVariableData $variableData )
602
	{
603
		$type                = gettype( $variable );
604
		$variableData->type  = "UNKNOWN" . ( !empty( $type ) ? " ({$type})" : '' );
605
		$variableData->value = var_export( $variable, true );
606
	}
607
608
}