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

JsonPointer   B

Complexity

Total Complexity 49

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Test Coverage

Coverage 92.71%

Importance

Changes 0
Metric Value
wmc 49
dl 0
loc 193
ccs 89
cts 96
cp 0.9271
rs 8.5454
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
B splitPath() 0 20 5
A escapeSegment() 0 6 2
D add() 0 44 19
A arrayKeyExists() 0 12 4
A arrayGet() 0 9 3
C remove() 0 30 8
C get() 0 27 8

How to fix   Complexity   

Complex Class

Complex classes like JsonPointer often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use JsonPointer, and based on these observations, apply Extract Interface, too.

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
}