Completed
Push — master ( 94925e...81a403 )
by Viacheslav
11s
created

JsonPointer::add()   C

Complexity

Conditions 25
Paths 26

Size

Total Lines 55
Code Lines 40

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 33
CRAP Score 25.1163

Importance

Changes 0
Metric Value
dl 0
loc 55
ccs 33
cts 35
cp 0.9429
rs 6.4952
c 0
b 0
f 0
cc 25
eloc 40
nc 26
nop 4
crap 25.1163

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 21
    public static function escapeSegment($key, $isURIFragmentId = false)
14
    {
15 21
        if ($isURIFragmentId) {
16 3
            return str_replace(array('%7E', '%2F'), array('~0', '~1'), urlencode($key));
17
        } else {
18 19
            return str_replace(array('~', '/'), array('~0', '~1'), $key);
19
        }
20
    }
21
22
    /**
23
     * @param string[] $pathItems
24
     * @param bool $isURIFragmentId
25
     * @return string
26
     */
27 1
    public static function buildPath(array $pathItems, $isURIFragmentId = false)
28
    {
29 1
        $result = $isURIFragmentId ? '#' : '';
30 1
        foreach ($pathItems as $pathItem) {
31 1
            $result .= '/' . self::escapeSegment($pathItem, $isURIFragmentId);
32
        }
33 1
        return $result;
34
    }
35
36
    /**
37
     * @param string $path
38
     * @return string[]
39
     * @throws Exception
40
     */
41 95
    public static function splitPath($path)
42
    {
43 95
        $pathItems = explode('/', $path);
44 95
        $first = array_shift($pathItems);
45 95
        if ($first === '#') {
46 1
            return self::splitPathURIFragment($pathItems);
47
        } else {
48 95
            if ($first !== '') {
49
                throw new Exception('Path must start with "/": ' . $path);
50
            }
51 95
            return self::splitPathJsonString($pathItems);
52
        }
53
    }
54
55 1
    private static function splitPathURIFragment(array $pathItems)
56
    {
57 1
        $result = array();
58 1
        foreach ($pathItems as $key) {
59 1
            $key = str_replace(array('~1', '~0'), array('/', '~'), urldecode($key));
60 1
            $result[] = $key;
61
        }
62 1
        return $result;
63
    }
64
65 95
    private static function splitPathJsonString(array $pathItems)
66
    {
67 95
        $result = array();
68 95
        foreach ($pathItems as $key) {
69 91
            $key = str_replace(array('~1', '~0'), array('/', '~'), $key);
70 91
            $result[] = $key;
71
        }
72 95
        return $result;
73
    }
74
75
    /**
76
     * @param mixed $holder
77
     * @param string[] $pathItems
78
     * @param mixed $value
79
     * @param bool $recursively
80
     * @throws Exception
81
     */
82 69
    public static function add(&$holder, $pathItems, $value, $recursively = true)
83
    {
84 69
        $ref = &$holder;
85 69
        while (null !== $key = array_shift($pathItems)) {
86 65
            if ($ref instanceof \stdClass) {
87 45
                if (PHP_VERSION_ID < 71000 && '' === $key) {
88
                    throw new Exception('Empty property name is not supported by PHP <7.1',
89
                        Exception::EMPTY_PROPERTY_NAME_UNSUPPORTED);
90
                }
91
92 45
                if ($recursively) {
93 9
                    $ref = &$ref->$key;
94
                } else {
95 39
                    if (!isset($ref->$key) && count($pathItems)) {
96 3
                        throw new Exception('Non-existent path item: ' . $key);
97
                    } else {
98 42
                        $ref = &$ref->$key;
99
                    }
100
                }
101
            } else { // null or array
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
102 39
                $intKey = filter_var($key, FILTER_VALIDATE_INT);
103 39
                if ($ref === null && (false === $intKey || $intKey !== 0)) {
104 11
                    $key = (string)$key;
105 11
                    if ($recursively) {
106 11
                        $ref = new \stdClass();
107 11
                        $ref = &$ref->{$key};
108
                    } else {
109 11
                        throw new Exception('Non-existent path item: ' . $key);
110
                    }
111 36
                } elseif ([] === $ref && false === $intKey && '-' !== $key) {
112 1
                    $ref = new \stdClass();
113 1
                    $ref = &$ref->{$key};
114
                } else {
115 35
                    if ($recursively && $ref === null) $ref = array();
116 35
                    if ('-' === $key) {
117 4
                        $ref = &$ref[];
118
                    } else {
119 32
                        if (is_array($ref) && array_key_exists($key, $ref) && empty($pathItems)) {
120 5
                            array_splice($ref, $key, 0, array($value));
121
                        }
122 32
                        if (false === $intKey) {
123 2
                            throw new Exception('Invalid key for array operation');
124
                        }
125 30
                        if ($intKey > count($ref) && !$recursively) {
126 2
                            throw new Exception('Index is greater than number of items in array');
127 28
                        } elseif ($intKey < 0) {
128 1
                            throw new Exception('Negative index');
129
                        }
130
131 27
                        $ref = &$ref[$intKey];
132
                    }
133
                }
134
            }
135
        }
136 61
        $ref = $value;
137 61
    }
138
139 43
    private static function arrayKeyExists($key, array $a)
140
    {
141 43
        if (array_key_exists($key, $a)) {
142 39
            return true;
143
        }
144 8
        $key = (string)$key;
145 8
        foreach ($a as $k => $v) {
146 8
            if ((string)$k === $key) {
147 8
                return true;
148
            }
149
        }
150 8
        return false;
151
    }
152
153 31
    private static function arrayGet($key, array $a)
154
    {
155 31
        $key = (string)$key;
156 31
        foreach ($a as $k => $v) {
157 31
            if ((string)$k === $key) {
158 31
                return $v;
159
            }
160
        }
161
        return false;
162
    }
163
164
165
    /**
166
     * @param mixed $holder
167
     * @param string[] $pathItems
168
     * @return bool|mixed
169
     * @throws Exception
170
     */
171 44
    public static function get($holder, $pathItems)
172
    {
173 44
        $ref = $holder;
174 44
        while (null !== $key = array_shift($pathItems)) {
175 43
            if ($ref instanceof \stdClass) {
176 32
                if (PHP_VERSION_ID < 71000 && '' === $key) {
177
                    throw new Exception('Empty property name is not supported by PHP <7.1',
178
                        Exception::EMPTY_PROPERTY_NAME_UNSUPPORTED);
179
                }
180
181 32
                $vars = (array)$ref;
182 32
                if (self::arrayKeyExists($key, $vars)) {
183 31
                    $ref = self::arrayGet($key, $vars);
184
                } else {
185 32
                    throw new Exception('Key not found: ' . $key);
186
                }
187 22
            } elseif (is_array($ref)) {
188 22
                if (self::arrayKeyExists($key, $ref)) {
189 17
                    $ref = $ref[$key];
190
                } else {
191 22
                    throw new Exception('Key not found: ' . $key);
192
                }
193
            } else {
194
                throw new Exception('Key not found: ' . $key);
195
            }
196
        }
197 38
        return $ref;
198
    }
199
200
    /**
201
     * @param mixed $holder
202
     * @param string $pointer
203
     * @return bool|mixed
204
     * @throws Exception
205
     */
206 1
    public static function getByPointer($holder, $pointer)
207
    {
208 1
        return self::get($holder, self::splitPath($pointer));
209
    }
210
211
    /**
212
     * @param mixed $holder
213
     * @param string[] $pathItems
214
     * @return mixed
215
     * @throws Exception
216
     */
217 36
    public static function remove(&$holder, $pathItems)
218
    {
219 36
        $ref = &$holder;
220 36
        while (null !== $key = array_shift($pathItems)) {
221 35
            $parent = &$ref;
222 35
            $refKey = $key;
223 35
            if ($ref instanceof \stdClass) {
224 23
                if (property_exists($ref, $key)) {
225 22
                    $ref = &$ref->$key;
226
                } else {
227 23
                    throw new Exception('Key not found: ' . $key);
228
                }
229
            } else {
230 22
                if (array_key_exists($key, $ref)) {
231 19
                    $ref = &$ref[$key];
232
                } else {
233 3
                    throw new Exception('Key not found: ' . $key);
234
                }
235
            }
236
        }
237
238 32
        if (isset($parent) && isset($refKey)) {
239 31
            if ($parent instanceof \stdClass) {
240 19
                unset($parent->$refKey);
241
            } else {
242 15
                unset($parent[$refKey]);
243 15
                if ($refKey !== count($parent)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $refKey does not seem to be defined for all execution paths leading up to this point.
Loading history...
Comprehensibility Best Practice introduced by
The variable $parent does not seem to be defined for all execution paths leading up to this point.
Loading history...
244 15
                    $parent = array_values($parent);
0 ignored issues
show
Unused Code introduced by
The assignment to $parent is dead and can be removed.
Loading history...
245
                }
246
            }
247
        }
248 32
        return $ref;
249
    }
250
}
251