Passed
Pull Request — master (#3)
by Viacheslav
02:25
created

JsonPointer::remove()   C

Complexity

Conditions 8
Paths 11

Size

Total Lines 30
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 18
cts 18
cp 1
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 21
nc 11
nop 2
crap 8
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 17
    public static function escapeSegment($key, $isURIFragmentId = false)
14
    {
15 17
        if ($isURIFragmentId) {
16 1
            return str_replace(array('%2F', '%7E'), array('~0', '~1'), urlencode($key));
17
        } else {
18 16
            return str_replace(array('~', '/'), array('~0', '~1'), $key);
19
        }
20
    }
21
22
    /**
23
     * @param string $path
24
     * @return string[]
25
     * @throws Exception
26
     */
27 90
    public static function splitPath($path)
28
    {
29 90
        $pathItems = explode('/', $path);
30 90
        $first = array_shift($pathItems);
31 90
        $result = array();
32 90
        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 90
            if ($first !== '') {
39
                throw new Exception('Path must start with "/": ' . $path);
40
            }
41 90
            foreach ($pathItems as $key) {
42 86
                $key = str_replace(array('~1', '~0'), array('/', '~'), $key);
43 86
                $result[] = $key;
44
            }
45
        }
46 90
        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 43
    private static function arrayKeyExists($key, array $a)
103
    {
104 43
        if (array_key_exists($key, $a)) {
105 39
            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 32
    private static function arrayGet($key, array $a)
117
    {
118 32
        $key = (string)$key;
119 32
        foreach ($a as $k => $v) {
120 32
            if ((string)$k === $key) {
121 32
                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 44
    public static function get($holder, $pathItems)
135
    {
136 44
        $ref = $holder;
137 44
        while (null !== $key = array_shift($pathItems)) {
138 43
            if ($ref instanceof \stdClass) {
139 33
                $vars = (array)$ref;
140 33
                if (self::arrayKeyExists($key, $vars)) {
141 32
                    $ref = self::arrayGet($key, $vars);
142
                } else {
143 33
                    throw new Exception('Key not found: ' . $key);
144
                }
145 21
            } elseif (is_array($ref)) {
146 21
                if (self::arrayKeyExists($key, $ref)) {
147 16
                    $ref = $ref[$key];
148
                } else {
149 21
                    throw new Exception('Key not found: ' . $key);
150
                }
151
            } else {
152
                throw new Exception('Key not found: ' . $key);
153
            }
154
        }
155 38
        return $ref;
156
    }
157
158
    /**
159
     * @param mixed $holder
160
     * @param string[] $pathItems
161
     * @return mixed
162
     * @throws Exception
163
     */
164 33
    public static function remove(&$holder, $pathItems)
165
    {
166 33
        $ref = &$holder;
167 33
        while (null !== $key = array_shift($pathItems)) {
168 32
            $parent = &$ref;
169 32
            $refKey = $key;
170 32
            if ($ref instanceof \stdClass) {
171 22
                if (property_exists($ref, $key)) {
172 21
                    $ref = &$ref->$key;
173
                } else {
174 22
                    throw new Exception('Key not found: ' . $key);
175
                }
176
            } else {
177 19
                if (array_key_exists($key, $ref)) {
178 16
                    $ref = &$ref[$key];
179
                } else {
180 3
                    throw new Exception('Key not found: ' . $key);
181
                }
182
            }
183
        }
184
185 29
        if (isset($parent) && isset($refKey)) {
186 28
            if ($parent instanceof \stdClass) {
187 18
                unset($parent->$refKey);
188
            } else {
189 12
                unset($parent[$refKey]);
190 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...
191
            }
192
        }
193 29
        return $ref;
194
    }
195
}