Completed
Push — master ( 6bbd38...55e0d1 )
by Erin
10s
created

src/ObjectPath/ObjectPath.php (3 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
namespace Peridot\Leo\ObjectPath;
3
4
/**
5
 * ObjectPath is a utility for parsing object and array strings into
6
 * ObjectPathValues.
7
 *
8
 * @package Peridot\Leo\Utility
9
 */
10
class ObjectPath
11
{
12
    /**
13
     * The subject to match path expressions against.
14
     *
15
     * @var array|object
16
     */
17
    protected $subject;
18
19
    /**
20
     * A pattern for matching array keys
21
     *
22
     * @var string
23
     */
24
    private static $arrayKey = '/\[([^\]]+)\]/';
25
26
    /**
27
     * @param array|object $subject
28
     */
29
    public function __construct($subject)
30
    {
31
        $this->subject = $subject;
32
    }
33
34
    /**
35
     * Returns an ObjectPathValue if the property described by $path
36
     * can be located in the subject.
37
     *
38
     * A path expression uses object and array syntax.
39
     *
40
     * @code
41
     *
42
     * $person = new stdClass();
43
     * $person->name = new stdClass();
44
     * $person->name->first = 'brian';
45
     * $person->name->last = 'scaturro';
46
     * $person->hobbies = ['programming', 'reading', 'board games'];
47
     *
48
     * $path = new ObjectPath($person);
49
     * $first = $path->get('name->first');
50
     * $reading = $path->get('hobbies[0]');
51
     *
52
     * @endcode
53
     *
54
     * @param string $path
55
     * @return ObjectPathValue
56
     */
57
    public function get($path)
58
    {
59
        $parts = $this->getPathParts($path);
60
        $properties = $this->getPropertyCollection($this->subject);
61
        $pathValue = null;
62
        while ($properties && $parts) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $properties of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
Bug Best Practice introduced by
The expression $parts of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
63
            $key = array_shift($parts);
64
            $key = $this->normalizeKey($key);
65
            $pathValue = array_key_exists($key, $properties) ? new ObjectPathValue($key, $properties[$key]) : null;
66
67
            if (! array_key_exists($key, $properties)) {
68
                break;
69
            }
70
71
            $properties = $this->getPropertyCollection($properties[$key]);
72
        }
73
        return $pathValue;
74
    }
75
76
    /**
77
     * Breaks a path expression into an array used
78
     * for navigating a path.
79
     *
80
     * @param $path
81
     * @return array
82
     */
83
    public function getPathParts($path)
84
    {
85
        $path = preg_replace('/\[/', '->[', $path);
86
        if (preg_match('/^->/', $path)) {
87
            $path = substr($path, 2);
88
        }
89
90
        return explode('->', $path);
91
    }
92
93
    /**
94
     * Returns a property as an array.
95
     *
96
     * @param $subject
97
     * @return array
98
     */
99
    protected function getPropertyCollection($subject)
100
    {
101
        if (is_object($subject)) {
102
            return get_object_vars($subject);
103
        }
104
105
        return $subject;
106
    }
107
108
    /**
109
     * Return a key that can be used on the current subject.
110
     *
111
     * @param $key
112
     * @param $matches
113
     * @return mixed
114
     */
115
    protected function normalizeKey($key)
116
    {
117
        if (preg_match(static::$arrayKey, $key, $matches)) {
0 ignored issues
show
Since $arrayKey is declared private, accessing it with static will lead to errors in possible sub-classes; consider using self, or increasing the visibility of $arrayKey to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return static::$someVariable;
    }
}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass { }

YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class SomeClass
{
    private static $someVariable;

    public static function getSomeVariable()
    {
        return self::$someVariable; // self works fine with private.
    }
}
Loading history...
118
            $key = $matches[1];
119
            return $key;
120
        }
121
        return $key;
122
    }
123
}
124