Test Setup Failed
Push — test ( dd7d7c...b3915a )
by Jonathan
03:06
created

Parser::parseObject()   D

Complexity

Conditions 15
Paths 68

Size

Total Lines 103
Code Lines 62

Duplication

Lines 15
Ratio 14.56 %

Importance

Changes 0
Metric Value
cc 15
eloc 62
nc 68
nop 2
dl 15
loc 103
rs 4.9121
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
namespace Kint\Parser;
4
5
use DomainException;
6
use Exception;
7
use Kint\Object\BasicObject;
8
use Kint\Object\BlobObject;
9
use Kint\Object\InstanceObject;
10
use Kint\Object\Representation\Representation;
11
use Kint\Object\ResourceObject;
12
use ReflectionObject;
13
14
class Parser
0 ignored issues
show
Coding Style introduced by
The property $caller_class is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $depth_limit is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $object_hashes is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style introduced by
The property $parse_break is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
15
{
16
    protected $caller_class = null;
0 ignored issues
show
Coding Style introduced by
$caller_class does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
17
    protected $depth_limit = false;
0 ignored issues
show
Coding Style introduced by
$depth_limit does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
18
    protected $marker;
19
    protected $object_hashes = array();
0 ignored issues
show
Coding Style introduced by
$object_hashes does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
20
    protected $parse_break = false;
0 ignored issues
show
Coding Style introduced by
$parse_break does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
21
    protected $plugins = array();
22
23
    /**
24
     * Plugin triggers.
25
     *
26
     * These are constants indicating trigger points for plugins
27
     *
28
     * BEGIN: Before normal parsing
29
     * SUCCESS: After successful parsing
30
     * RECURSION: After parsing cancelled by recursion
31
     * DEPTH_LIMIT: After parsing cancelled by depth limit
32
     * COMPLETE: SUCCESS | RECURSION | DEPTH_LIMIT
33
     *
34
     * While a plugin's getTriggers may return any of these
35
     */
36
    const TRIGGER_NONE = 0;
37
    const TRIGGER_BEGIN = 1;
38
    const TRIGGER_SUCCESS = 2;
39
    const TRIGGER_RECURSION = 4;
40
    const TRIGGER_DEPTH_LIMIT = 8;
41
    const TRIGGER_COMPLETE = 14;
42
43
    /**
44
     * @param int|false   $depth_limit Maximum depth to parse data
45
     * @param string|null $caller      Caller class name
46
     */
47
    public function __construct($depth_limit = false, $caller = null)
0 ignored issues
show
Coding Style introduced by
$depth_limit does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Coding Style Naming introduced by
The parameter $depth_limit is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
48
    {
49
        $this->marker = uniqid("kint\0", true);
50
51
        $this->caller_class = $caller;
52
53
        if ($depth_limit) {
0 ignored issues
show
Coding Style introduced by
$depth_limit does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Bug Best Practice introduced by
The expression $depth_limit of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
54
            $this->depth_limit = $depth_limit;
0 ignored issues
show
Coding Style introduced by
$depth_limit does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Documentation Bug introduced by
The property $depth_limit was declared of type boolean, but $depth_limit is of type integer. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
55
        }
56
    }
57
58
    /**
59
     * Set the caller class.
60
     *
61
     * @param string|null $caller Caller class name
62
     */
63
    public function setCallerClass($caller = null)
64
    {
65
        $this->noRecurseCall();
66
67
        $this->caller_class = $caller;
68
    }
69
70
    public function getCallerClass()
71
    {
72
        return $this->caller_class;
73
    }
74
75
    /**
76
     * Set the depth limit.
77
     *
78
     * @param int|false $depth_limit Maximum depth to parse data
79
     */
80
    public function setDepthLimit($depth_limit = false)
0 ignored issues
show
Coding Style introduced by
$depth_limit does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Coding Style Naming introduced by
The parameter $depth_limit is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
81
    {
82
        $this->noRecurseCall();
83
84
        $this->depth_limit = $depth_limit;
0 ignored issues
show
Coding Style introduced by
$depth_limit does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Documentation Bug introduced by
It seems like $depth_limit can also be of type integer. However, the property $depth_limit is declared as type boolean. 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...
85
    }
86
87
    public function getDepthLimit()
0 ignored issues
show
Coding Style introduced by
function getDepthLimit() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
88
    {
89
        return $this->depth_limit;
90
    }
91
92
    protected function noRecurseCall()
93
    {
94
        $bt = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $bt. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
95
96
        $caller_frame = array(
0 ignored issues
show
Coding Style introduced by
$caller_frame does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
97
            'function' => __FUNCTION__,
98
        );
99
100
        while (isset($bt[0]['object']) && $bt[0]['object'] === $this) {
101
            $caller_frame = array_shift($bt);
0 ignored issues
show
Coding Style introduced by
$caller_frame does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
102
        }
103
104
        foreach ($bt as $frame) {
105
            if (isset($frame['object']) && $frame['object'] === $this) {
106
                throw new DomainException(__CLASS__.'::'.$caller_frame['function'].' cannot be called from inside a parse');
0 ignored issues
show
Coding Style introduced by
$caller_frame does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
107
            }
108
        }
109
    }
110
111
    /**
112
     * Disables the depth limit and parses a variable.
113
     *
114
     * This should not be used unless you know what you're doing!
115
     *
116
     * @param mixed       $var The input variable
117
     * @param BasicObject $o   The base object
118
     *
119
     * @return BasicObject
120
     */
121
    public function parseDeep(&$var, BasicObject $o)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $o. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
122
    {
123
        $depth_limit = $this->depth_limit;
0 ignored issues
show
Coding Style introduced by
$depth_limit does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
124
        $this->depth_limit = false;
125
126
        $out = $this->parse($var, $o);
127
128
        $this->depth_limit = $depth_limit;
0 ignored issues
show
Coding Style introduced by
$depth_limit does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
129
130
        return $out;
131
    }
132
133
    /**
134
     * Parses a variable into a Kint object structure.
135
     *
136
     * @param mixed       $var The input variable
137
     * @param BasicObject $o   The base object
138
     *
139
     * @return BasicObject
140
     */
141
    public function parse(&$var, BasicObject $o)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $o. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
142
    {
143
        $o->type = strtolower(gettype($var));
144
145
        if (!$this->applyPlugins($var, $o, self::TRIGGER_BEGIN)) {
146
            return $o;
147
        }
148
149
        switch ($o->type) {
150
            case 'array':
151
                return $this->parseArray($var, $o);
152
            case 'boolean':
153
            case 'double':
154
            case 'integer':
155
            case 'null':
156
                return $this->parseGeneric($var, $o);
157
            case 'object':
158
                return $this->parseObject($var, $o);
159
            case 'resource':
160
                return $this->parseResource($var, $o);
161
            case 'string':
162
                return $this->parseString($var, $o);
163
            default:
164
                return $this->parseUnknown($var, $o);
165
        }
166
    }
167
168
    private function parseGeneric(&$var, BasicObject $o)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $o. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
169
    {
170
        $rep = new Representation('Contents');
171
        $rep->contents = $var;
172
        $rep->implicit_label = true;
173
        $o->addRepresentation($rep);
174
        $o->value = $rep;
175
176
        $this->applyPlugins($var, $o, self::TRIGGER_SUCCESS);
177
178
        return $o;
179
    }
180
181
    /**
182
     * Parses a string into a Kint BlobObject structure.
183
     *
184
     * @param string      $var The input variable
185
     * @param BasicObject $o   The base object
186
     *
187
     * @return BlobObject
188
     */
189
    private function parseString(&$var, BasicObject $o)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $o. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
190
    {
191
        $string = $o->transplant(new BlobObject());
192
        $string->encoding = BlobObject::detectEncoding($var);
0 ignored issues
show
Bug introduced by
The property encoding does not seem to exist in Kint\Object\BasicObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
193
        $string->size = BlobObject::strlen($var, $string->encoding);
194
195
        $rep = new Representation('Contents');
196
        $rep->contents = $var;
0 ignored issues
show
Documentation Bug introduced by
It seems like $var of type string is incompatible with the declared type array of property $contents.

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...
197
        $rep->implicit_label = true;
198
199
        $string->addRepresentation($rep);
200
        $string->value = $rep;
201
202
        $this->applyPlugins($var, $string, self::TRIGGER_SUCCESS);
203
204
        return $string;
205
    }
206
207
    /**
208
     * Parses an array into a Kint object structure.
209
     *
210
     * @param array       $var The input variable
211
     * @param BasicObject $o   The base object
212
     *
213
     * @return BasicObject
214
     */
215
    private function parseArray(array &$var, BasicObject $o)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $o. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
216
    {
217
        $array = $o->transplant(new BasicObject());
218
        $array->size = count($var);
219
220 View Code Duplication
        if (isset($var[$this->marker])) {
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...
221
            --$array->size;
222
            $array->hints[] = 'recursion';
223
224
            $this->applyPlugins($var, $array, self::TRIGGER_RECURSION);
225
226
            return $array;
227
        }
228
229
        $rep = new Representation('Contents');
230
        $rep->implicit_label = true;
231
        $array->addRepresentation($rep);
232
        $array->value = $rep;
233
234
        if ($array->size) {
235 View Code Duplication
            if ($this->depth_limit && $o->depth >= $this->depth_limit) {
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...
236
                $array->hints[] = 'depth_limit';
237
238
                $this->applyPlugins($var, $array, self::TRIGGER_DEPTH_LIMIT);
239
240
                return $array;
241
            }
242
243
            $copy = array_values($var);
244
245
            // It's really really hard to access numeric string keys in arrays,
246
            // and it's really really hard to access integer properties in
247
            // objects, so we just use array_values and index by counter to get
248
            // at it reliably for reference testing. This also affects access
249
            // paths since it's pretty much impossible to access these things
250
            // without complicated stuff you should never need to do.
251
            $i = 0;
252
253
            // Set the marker for recursion
254
            $var[$this->marker] = $array->depth;
255
256
            foreach ($var as $key => &$val) {
257
                if ($key === $this->marker) {
258
                    continue;
259
                }
260
261
                $child = new BasicObject();
262
                $child->name = $key;
263
                $child->depth = $array->depth + 1;
264
                $child->access = BasicObject::ACCESS_NONE;
265
                $child->operator = BasicObject::OPERATOR_ARRAY;
266
267
                if ($array->access_path !== null) {
268
                    if (is_string($key) && (string) (int) $key === $key) {
269
                        $child->access_path = 'array_values('.$array->access_path.')['.$i.']';
270
                    } else {
271
                        $child->access_path = $array->access_path.'['.var_export($key, true).']';
272
                    }
273
                }
274
275
                $stash = $val;
276
                $copy[$i] = $this->marker;
277
                if ($val === $this->marker) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison === seems to always evaluate to false as the types of $val (integer) and $this->marker (string) can never be identical. Maybe you want to use a loose comparison == instead?
Loading history...
278
                    $child->reference = true;
279
                    $val = $stash;
280
                }
281
282
                $rep->contents[] = $this->parse($val, $child);
283
                ++$i;
284
            }
285
286
            $this->applyPlugins($var, $array, self::TRIGGER_SUCCESS);
287
            unset($var[$this->marker]);
288
289
            return $array;
290
        } else {
291
            $this->applyPlugins($var, $array, self::TRIGGER_SUCCESS);
292
293
            return $array;
294
        }
295
    }
296
297
    /**
298
     * Parses an object into a Kint InstanceObject structure.
299
     *
300
     * @param object      $var The input variable
301
     * @param BasicObject $o   The base object
302
     *
303
     * @return InstanceObject
304
     */
305
    private function parseObject(&$var, BasicObject $o)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $o. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
306
    {
307
        $hash = spl_object_hash($var);
308
        $values = (array) $var;
309
310
        $object = $o->transplant(new InstanceObject());
311
        $object->classname = get_class($var);
0 ignored issues
show
Bug introduced by
The property classname does not seem to exist in Kint\Object\BasicObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
312
        $object->hash = $hash;
0 ignored issues
show
Bug introduced by
The property hash does not seem to exist in Kint\Object\BasicObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
313
        $object->size = count($values);
314
315 View Code Duplication
        if (isset($this->object_hashes[$hash])) {
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...
316
            $object->hints[] = 'recursion';
317
318
            $this->applyPlugins($var, $object, self::TRIGGER_RECURSION);
319
320
            return $object;
321
        }
322
323
        $this->object_hashes[$hash] = $object;
324
325 View Code Duplication
        if ($this->depth_limit && $o->depth >= $this->depth_limit) {
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...
326
            $object->hints[] = 'depth_limit';
327
328
            $this->applyPlugins($var, $object, self::TRIGGER_DEPTH_LIMIT);
329
            unset($this->object_hashes[$hash]);
330
331
            return $object;
332
        }
333
334
        $reflector = new ReflectionObject($var);
335
336
        if ($reflector->isUserDefined()) {
337
            $object->filename = $reflector->getFileName();
0 ignored issues
show
Bug introduced by
The property filename does not seem to exist in Kint\Object\BasicObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
338
            $object->startline = $reflector->getStartLine();
0 ignored issues
show
Bug introduced by
The property startline does not seem to exist in Kint\Object\BasicObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
339
        }
340
341
        $rep = new Representation('Properties');
342
343
        $copy = array_values($values);
344
345
        $i = 0;
346
347
        // Reflection will not show parent classes private properties, and if a
348
        // property was unset it will happly trigger a notice looking for it.
349
        foreach ($values as $key => &$val) {
350
            // Casting object to array:
351
            // private properties show in the form "\0$owner_class_name\0$property_name";
352
            // protected properties show in the form "\0*\0$property_name";
353
            // public properties show in the form "$property_name";
354
            // http://www.php.net/manual/en/language.types.array.php#language.types.array.casting
355
356
            $child = new BasicObject();
357
            $child->depth = $object->depth + 1;
358
            $child->owner_class = $object->classname;
359
            $child->operator = BasicObject::OPERATOR_OBJECT;
360
            $child->access = BasicObject::ACCESS_PUBLIC;
361
362
            $split_key = explode("\0", $key, 3);
0 ignored issues
show
Coding Style introduced by
$split_key does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
363
364
            if (count($split_key) === 3 && $split_key[0] === '') {
0 ignored issues
show
Coding Style introduced by
$split_key does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
365
                $child->name = $split_key[2];
0 ignored issues
show
Coding Style introduced by
$split_key does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
366
                if ($split_key[1] === '*') {
0 ignored issues
show
Coding Style introduced by
$split_key does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
367
                    $child->access = BasicObject::ACCESS_PROTECTED;
368
                } else {
369
                    $child->access = BasicObject::ACCESS_PRIVATE;
370
                    $child->owner_class = $split_key[1];
0 ignored issues
show
Coding Style introduced by
$split_key does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
371
                }
372
            } elseif (KINT_PHP72) {
373
                $child->name = (string) $key; // @codeCoverageIgnore
374
            } else {
375
                $child->name = $key;
376
            }
377
378
            if ($this->childHasPath($object, $child)) {
0 ignored issues
show
Compatibility introduced by
$object of type object<Kint\Object\BasicObject> is not a sub-type of object<Kint\Object\InstanceObject>. It seems like you assume a child class of the class Kint\Object\BasicObject to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
379
                $child->access_path = $object->access_path;
380
381
                if (!KINT_PHP72 && is_int($child->name)) {
382
                    $child->access_path = 'array_values((array) '.$child->access_path.')['.$i.']';
383
                } elseif (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $child->name)) {
384
                    $child->access_path .= '->'.$child->name;
385
                } else {
386
                    $child->access_path .= '->{'.var_export((string) $child->name, true).'}';
387
                }
388
            }
389
390
            $stash = $val;
391
            $copy[$i] = $this->marker;
392
            if ($val === $this->marker) {
393
                $child->reference = true;
394
                $val = $stash;
395
            }
396
397
            $rep->contents[] = $this->parse($val, $child);
398
            ++$i;
399
        }
400
401
        $object->addRepresentation($rep);
402
        $object->value = $rep;
403
        $this->applyPlugins($var, $object, self::TRIGGER_SUCCESS);
404
        unset($this->object_hashes[$hash]);
405
406
        return $object;
407
    }
408
409
    /**
410
     * Parses a resource into a Kint ResourceObject structure.
411
     *
412
     * @param resource    $var The input variable
413
     * @param BasicObject $o   The base object
414
     *
415
     * @return ResourceObject
416
     */
417
    private function parseResource(&$var, BasicObject $o)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $o. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
418
    {
419
        $resource = $o->transplant(new ResourceObject());
420
        $resource->resource_type = get_resource_type($var);
0 ignored issues
show
Bug introduced by
The property resource_type does not seem to exist in Kint\Object\BasicObject.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
421
422
        $this->applyPlugins($var, $resource, self::TRIGGER_SUCCESS);
423
424
        return $resource;
425
    }
426
427
    /**
428
     * Parses an unknown into a Kint object structure.
429
     *
430
     * @param mixed       $var The input variable
431
     * @param BasicObject $o   The base object
432
     *
433
     * @return BasicObject
434
     */
435
    private function parseUnknown(&$var, BasicObject $o)
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $o. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
436
    {
437
        $o->type = 'unknown';
438
        $this->applyPlugins($var, $o, self::TRIGGER_SUCCESS);
439
440
        return $o;
441
    }
442
443
    public function addPlugin(Plugin $p)
0 ignored issues
show
Coding Style introduced by
function addPlugin() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $p. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
444
    {
445
        if (!$types = $p->getTypes()) {
446
            return false;
447
        }
448
449
        if (!$triggers = $p->getTriggers()) {
450
            return false;
451
        }
452
453
        $p->setParser($this);
454
455
        foreach ($types as $type) {
456
            if (!isset($this->plugins[$type])) {
457
                $this->plugins[$type] = array(
458
                    self::TRIGGER_BEGIN => array(),
459
                    self::TRIGGER_SUCCESS => array(),
460
                    self::TRIGGER_RECURSION => array(),
461
                    self::TRIGGER_DEPTH_LIMIT => array(),
462
                );
463
            }
464
465
            foreach ($this->plugins[$type] as $trigger => &$pool) {
466
                if ($triggers & $trigger) {
467
                    $pool[] = $p;
468
                }
469
            }
470
        }
471
472
        return true;
473
    }
474
475
    public function clearPlugins()
476
    {
477
        $this->plugins = array();
478
    }
479
480
    /**
481
     * Applies plugins for an object type.
482
     *
483
     * @param mixed       $var     variable
484
     * @param BasicObject $o       Kint object parsed so far
485
     * @param int         $trigger The trigger to check for the plugins
486
     *
487
     * @return bool Continue parsing
488
     */
489
    private function applyPlugins(&$var, BasicObject &$o, $trigger)
0 ignored issues
show
Coding Style introduced by
function applyPlugins() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
Comprehensibility introduced by
Avoid variables with short names like $o. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
490
    {
491
        $break_stash = $this->parse_break;
0 ignored issues
show
Coding Style introduced by
$break_stash does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
492
        $this->parse_break = false;
493
494
        $plugins = array();
495
496
        if (isset($this->plugins[$o->type][$trigger])) {
497
            $plugins = $this->plugins[$o->type][$trigger];
498
        }
499
500
        foreach ($plugins as $plugin) {
501
            try {
502
                $plugin->parse($var, $o, $trigger);
503
            } catch (Exception $e) {
504
                trigger_error(
505
                    'An exception ('.get_class($e).') was thrown in '.$e->getFile().' on line '.$e->getLine().' while executing Kint Parser Plugin "'.get_class($plugin).'". Error message: '.$e->getMessage(),
506
                    E_USER_WARNING
507
                );
508
            }
509
510
            if ($this->parse_break) {
511
                $this->parse_break = $break_stash;
0 ignored issues
show
Coding Style introduced by
$break_stash does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
512
513
                return false;
514
            }
515
        }
516
517
        $this->parse_break = $break_stash;
0 ignored issues
show
Coding Style introduced by
$break_stash does not seem to conform to the naming convention (^[a-z][a-zA-Z0-9]*$).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
518
519
        return true;
520
    }
521
522
    public function haltParse()
523
    {
524
        $this->parse_break = true;
525
    }
526
527
    public function childHasPath(InstanceObject $parent, BasicObject $child)
0 ignored issues
show
Coding Style introduced by
function childHasPath() does not seem to conform to the naming convention (^(?:is|has|should|may|supports)).

This check examines a number of code elements and verifies that they conform to the given naming conventions.

You can set conventions for local variables, abstract classes, utility classes, constant, properties, methods, parameters, interfaces, classes, exceptions and special methods.

Loading history...
528
    {
529
        if ($parent->type === 'object' && ($parent->access_path !== null || $child->static || $child->const)) {
530
            if ($child->access === BasicObject::ACCESS_PUBLIC) {
531
                return true;
532
            } elseif ($child->access === BasicObject::ACCESS_PRIVATE && $this->caller_class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->caller_class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
533
                if ($this->caller_class === $child->owner_class) {
534
                    return true;
535
                }
536
            } elseif ($child->access === BasicObject::ACCESS_PROTECTED && $this->caller_class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->caller_class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
537
                if ($this->caller_class === $child->owner_class) {
538
                    return true;
539
                }
540
                if (is_subclass_of($this->caller_class, $child->owner_class)) {
541
                    return true;
542
                }
543
                if (is_subclass_of($child->owner_class, $this->caller_class)) {
544
                    return true;
545
                }
546
            }
547
        }
548
549
        return false;
550
    }
551
552
    /**
553
     * Returns an array without the recursion marker in it.
554
     *
555
     * DO NOT pass an array that has had it's marker removed back
556
     * into the parser, it will result in an extra recursion
557
     *
558
     * @param array $array Array potentially containing a recursion marker
559
     *
560
     * @return array Array with recursion marker removed
561
     */
562
    public function getCleanArray(array $array)
563
    {
564
        unset($array[$this->marker]);
565
566
        return $array;
567
    }
568
}
569