Completed
Push — master ( 205892...e583f8 )
by Viacheslav
9s
created

JsonPointer::splitPath()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0175

Importance

Changes 0
Metric Value
dl 0
loc 11
ccs 7
cts 8
cp 0.875
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 1
crap 3.0175
1
<?php
2
3
namespace Swaggest\JsonDiff;
4
5
6
class JsonPointer
7
{
8
    /**
9
     * @param string $key
10
     * @param bool $isURIFragmentId
11
     * @return string
12
     */
13 16
    public static function escapeSegment($key, $isURIFragmentId = false)
14
    {
15 16
        if ($isURIFragmentId) {
16 1
            return str_replace(array('%2F', '%7E'), array('~0', '~1'), urlencode($key));
17
        } else {
18 15
            return str_replace(array('~', '/'), array('~0', '~1'), $key);
19
        }
20
    }
21
22
    /**
23
     * @param string $path
24
     * @return string[]
25
     * @throws Exception
26
     */
27 88
    public static function splitPath($path)
28
    {
29 88
        $pathItems = explode('/', $path);
30 88
        $first = array_shift($pathItems);
31 88
        if ($first === '#') {
32 1
            return self::splitPathURIFragment($pathItems);
33
        } else {
34 88
            if ($first !== '') {
35
                throw new Exception('Path must start with "/": ' . $path);
36
            }
37 88
            return self::splitPathJsonString($pathItems);
38
        }
39
    }
40
41 1
    private static function splitPathURIFragment(array $pathItems) {
42 1
        $result = array();
43 1
        foreach ($pathItems as $key) {
44 1
            $key = str_replace(array('~1', '~0'), array('/', '~'), urldecode($key));
45 1
            $result[] = $key;
46
        }
47 1
        return $result;
48
    }
49
50 88
    private static function splitPathJsonString(array $pathItems) {
51 88
        $result = array();
52 88
        foreach ($pathItems as $key) {
53 84
            $key = str_replace(array('~1', '~0'), array('/', '~'), $key);
54 84
            $result[] = $key;
55
        }
56 88
        return $result;
57
    }
58
59
    /**
60
     * @param mixed $holder
61
     * @param string[] $pathItems
62
     * @param mixed $value
63
     * @param bool $recursively
64
     * @throws Exception
65
     */
66 67
    public static function add(&$holder, $pathItems, $value, $recursively = true)
67
    {
68 67
        $ref = &$holder;
69 67
        while (null !== $key = array_shift($pathItems)) {
70 63
            if ($ref instanceof \stdClass) {
71 44
                if (PHP_VERSION_ID < 71000 && '' === $key) {
72
                    throw new Exception('Empty property name is not supported by PHP <7.1',
73
                        Exception::EMPTY_PROPERTY_NAME_UNSUPPORTED);
74
                }
75
76 44
                $ref = &$ref->$key;
77
            } else { // null or array
78 41
                $intKey = filter_var($key, FILTER_VALIDATE_INT);
79 41
                if ($ref === null && (false === $intKey || $intKey !== 0)) {
80 13
                    $key = (string)$key;
81 13
                    if ($recursively) {
82 11
                        $ref = new \stdClass();
83 11
                        $ref = &$ref->{$key};
84
                    } else {
85 13
                        throw new Exception('Non-existent path');
86
                    }
87
                } else {
88 36
                    if ($recursively && $ref === null) $ref = array();
89 36
                    if ('-' === $key) {
90 4
                        $ref = &$ref[];
91
                    } else {
92 33
                        if (is_array($ref) && array_key_exists($key, $ref) && empty($pathItems)) {
93 6
                            array_splice($ref, $key, 0, array($value));
94
                        }
95 33
                        if (false === $intKey) {
96 2
                            throw new Exception('Invalid key for array operation');
97
                        }
98 31
                        if ($intKey > count($ref) && !$recursively) {
99 2
                            throw new Exception('Index is greater than number of items in array');
100 29
                        } elseif ($intKey < 0) {
101 1
                            throw new Exception('Negative index');
102
                        }
103
104 28
                        $ref = &$ref[$intKey];
105
                    }
106
                }
107
            }
108
        }
109 60
        $ref = $value;
110 60
    }
111
112 41
    private static function arrayKeyExists($key, array $a)
113
    {
114 41
        if (array_key_exists($key, $a)) {
115 37
            return true;
116
        }
117 7
        $key = (string)$key;
118 7
        foreach ($a as $k => $v) {
119 7
            if ((string)$k === $key) {
120 7
                return true;
121
            }
122
        }
123 7
        return false;
124
    }
125
126 30
    private static function arrayGet($key, array $a)
127
    {
128 30
        $key = (string)$key;
129 30
        foreach ($a as $k => $v) {
130 30
            if ((string)$k === $key) {
131 30
                return $v;
132
            }
133
        }
134
        return false;
135
    }
136
137
138
    /**
139
     * @param mixed $holder
140
     * @param string[] $pathItems
141
     * @return bool|mixed
142
     * @throws Exception
143
     */
144 42
    public static function get($holder, $pathItems)
145
    {
146 42
        $ref = $holder;
147 42
        while (null !== $key = array_shift($pathItems)) {
148 41
            if ($ref instanceof \stdClass) {
149 31
                if (PHP_VERSION_ID < 71000 && '' === $key) {
150
                    throw new Exception('Empty property name is not supported by PHP <7.1',
151
                        Exception::EMPTY_PROPERTY_NAME_UNSUPPORTED);
152
                }
153
154 31
                $vars = (array)$ref;
155 31
                if (self::arrayKeyExists($key, $vars)) {
156 30
                    $ref = self::arrayGet($key, $vars);
157
                } else {
158 31
                    throw new Exception('Key not found: ' . $key);
159
                }
160 20
            } elseif (is_array($ref)) {
161 20
                if (self::arrayKeyExists($key, $ref)) {
162 15
                    $ref = $ref[$key];
163
                } else {
164 20
                    throw new Exception('Key not found: ' . $key);
165
                }
166
            } else {
167
                throw new Exception('Key not found: ' . $key);
168
            }
169
        }
170 36
        return $ref;
171
    }
172
173
    /**
174
     * @param mixed $holder
175
     * @param string[] $pathItems
176
     * @return mixed
177
     * @throws Exception
178
     */
179 33
    public static function remove(&$holder, $pathItems)
180
    {
181 33
        $ref = &$holder;
182 33
        while (null !== $key = array_shift($pathItems)) {
183 32
            $parent = &$ref;
184 32
            $refKey = $key;
185 32
            if ($ref instanceof \stdClass) {
186 22
                if (property_exists($ref, $key)) {
187 21
                    $ref = &$ref->$key;
188
                } else {
189 22
                    throw new Exception('Key not found: ' . $key);
190
                }
191
            } else {
192 19
                if (array_key_exists($key, $ref)) {
193 16
                    $ref = &$ref[$key];
194
                } else {
195 3
                    throw new Exception('Key not found: ' . $key);
196
                }
197
            }
198
        }
199
200 29
        if (isset($parent) && isset($refKey)) {
201 28
            if ($parent instanceof \stdClass) {
202 18
                unset($parent->$refKey);
203
            } else {
204 12
                unset($parent[$refKey]);
205 12
                $parent = array_values($parent);
0 ignored issues
show
Unused Code introduced by
The assignment to $parent is dead and can be removed.
Loading history...
206
            }
207
        }
208 29
        return $ref;
209
    }
210
}