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) { |
|
|
|
|
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)) { |
|
|
|
|
118
|
|
|
$key = $matches[1]; |
119
|
|
|
return $key; |
120
|
|
|
} |
121
|
|
|
return $key; |
122
|
|
|
} |
123
|
|
|
} |
124
|
|
|
|
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.