Completed
Push — master ( 16f044...205892 )
by Viacheslav
10s
created

JsonPointer::get()   C

Complexity

Conditions 8
Paths 7

Size

Total Lines 27
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 8.3518

Importance

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