Completed
Pull Request — master (#3)
by Viacheslav
02:07
created

JsonPointer   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 183
Duplicated Lines 0 %

Test Coverage

Coverage 96.67%

Importance

Changes 0
Metric Value
wmc 45
dl 0
loc 183
ccs 87
cts 90
cp 0.9667
rs 8.3673
c 0
b 0
f 0

7 Methods

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

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