Completed
Push — master ( ff013c...75a318 )
by Erin
02:17
created

ObjectPath::getPathValue()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 2
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
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 (!empty($properties) && !empty($parts)) {
63
            $key = array_shift($parts);
64
            $key = $this->normalizeKey($key);
65
            $pathValue = $this->getPathValue($key, $properties);
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(self::$arrayKey, $key, $matches)) {
118
            $key = $matches[1];
119
            return $key;
120
        }
121
        return $key;
122
    }
123
124
    /**
125
     * Given a key and a collection of properties, this method
126
     * will return an ObjectPathValue if possible.
127
     *
128
     * @param $key
129
     * @param $properties
130
     * @return null|ObjectPathValue
131
     */
132
    protected function getPathValue($key, $properties)
133
    {
134
        if (! array_key_exists($key, $properties)) {
135
            return null;
136
        }
137
138
        return new ObjectPathValue($key, $properties[$key]);
139
    }
140
}
141