Completed
Pull Request — master (#12)
by Jan
02:43
created

Uri   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 307
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 100%

Importance

Changes 9
Bugs 2 Features 2
Metric Value
wmc 45
c 9
b 2
f 2
lcom 1
cbo 0
dl 0
loc 307
ccs 123
cts 123
cp 1
rs 8.3673

20 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A getRawUri() 0 4 1
A isAbsolute() 0 4 1
A getScheme() 0 4 1
A getAuthority() 0 4 1
A getPath() 0 4 1
A getQuery() 0 4 1
A getPointerSegments() 0 4 1
A getRawPointer() 0 4 1
A hasPointer() 0 4 1
A getPrimaryResourceIdentifier() 0 4 1
A resolveAgainst() 0 13 3
A isSamePrimaryResource() 0 8 3
B buildFromRawUri() 0 23 6
A buildAuthority() 0 19 4
A buildSegments() 0 16 3
B buildPrimaryIdentifier() 0 20 5
B buildResolvedUriAgainst() 0 24 4
A buildResolvedPathAgainst() 0 12 3
A appendRelativeParts() 0 12 3

How to fix   Complexity   

Complex Class

Complex classes like Uri 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Uri, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the JVal package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace JVal;
11
12
/**
13
 * Wraps a raw URI string, providing methods to deal with URI normalization,
14
 * comparison and resolution (including JSON pointers references).
15
 */
16
class Uri
17
{
18
    private static $partNames = [
19
        'scheme',
20
        'user',
21
        'pass',
22
        'host',
23
        'port',
24
        'path',
25
        'query',
26
        'fragment',
27
    ];
28
29
    /**
30
     * @var string
31
     */
32
    private $raw;
33
34
    /**
35
     * @var array
36
     */
37
    private $parts;
38
39
    /**
40
     * @var string
41
     */
42
    private $authority;
43
44
    /**
45
     * @var string[]
46
     */
47
    private $segments;
48
49
    /**
50
     * @var string
51
     */
52
    private $primaryIdentifier;
53
54
    /**
55
     * Constructor.
56
     *
57
     * @param string $rawUri
58
     */
59 388
    public function __construct($rawUri)
60
    {
61 388
        $this->buildFromRawUri($rawUri);
62 387
    }
63
64
    /**
65
     * @return string
66
     */
67 19
    public function getRawUri()
68
    {
69 19
        return $this->raw;
70
    }
71
72
    /**
73
     * @return string
74
     */
75 58
    public function getRawPointer()
76
    {
77 58
        return $this->parts['fragment'];
78
    }
79
80
    /**
81
     * @return bool
82
     */
83 376
    public function isAbsolute()
84
    {
85 376
        return $this->parts['scheme'] !== '';
86
    }
87
88
    /**
89
     * @return string
90
     */
91 59
    public function getScheme()
92
    {
93 59
        return $this->parts['scheme'];
94
    }
95
96
    /**
97
     * @return string
98
     */
99 59
    public function getAuthority()
100
    {
101 59
        return $this->authority;
102
    }
103
104
    /**
105
     * @return string
106
     */
107 59
    public function getPath()
108
    {
109 59
        return $this->parts['path'];
110
    }
111
112
    /**
113
     * @return string
114
     */
115 59
    public function getQuery()
116
    {
117 59
        return $this->parts['query'];
118
    }
119
120
    /**
121
     * @return string[]
122
     */
123 70
    public function getPointerSegments()
124
    {
125 70
        return $this->segments;
126
    }
127
128
    /**
129
     * @return bool
130
     */
131 337
    public function hasPointer()
132
    {
133 337
        return !empty($this->segments);
134
    }
135
136
    /**
137
     * Returns the primary resource identifier part of the URI, i.e. everything
138
     * excluding its fragment part.
139
     *
140
     * @return string
141
     */
142 354
    public function getPrimaryResourceIdentifier()
143
    {
144 354
        return $this->primaryIdentifier;
145
    }
146
147
    /**
148
     * Resolves the current (relative) URI against another (absolute) URI.
149
     * Example:.
150
     *
151
     * Current  = foo.json
152
     * Other    = http://localhost/bar/baz
153
     * Resolved = http://localhost/bar/foo.json
154
     *
155
     * @param Uri $uri
156
     *
157
     * @return Uri
158
     */
159 75
    public function resolveAgainst(Uri $uri)
160
    {
161 75
        if ($this->isAbsolute()) {
162 33
            return $this;
163 60
        } elseif (!$uri->isAbsolute()) {
164 1
            throw new \LogicException(
165
                'Cannot resolve against another URI: reference URI is not absolute'
166 1
            );
167
        } else {
168 59
            $resolvedUri = $this->buildResolvedUriAgainst($uri);
169 59
            return new self($resolvedUri);
170
        }
171
    }
172
173
    /**
174
     * Returns whether two URIs share the same primary resource identifier,
175
     * i.e. whether they point to the same document.
176
     *
177
     * @param Uri $uri
178
     *
179
     * @return bool
180
     */
181 5
    public function isSamePrimaryResource(Uri $uri)
182
    {
183 5
        if (!$this->isAbsolute() || !$uri->isAbsolute()) {
184 1
            throw new \LogicException('Cannot compare URIs: both must be absolute');
185
        }
186
187 4
        return $this->primaryIdentifier === $uri->getPrimaryResourceIdentifier();
188
    }
189
190 388
    private function buildFromRawUri($rawUri)
191
    {
192 388
        $this->raw = rawurldecode($rawUri);
193 388
        $this->parts = @parse_url($this->raw);
194
195 388
        if (false === $this->parts) {
196 1
            throw new \InvalidArgumentException("Cannot parse URI '{$rawUri}'");
197
        }
198
199 387
        foreach (self::$partNames as $part) {
200 387
            if (!isset($this->parts[$part])) {
201 387
                $this->parts[$part] = '';
202 387
            }
203 387
        }
204
205 387
        if ($this->parts['scheme'] === 'file' && preg_match('/^[A-Z]:/i', $this->parts['path'])) {
206 2
            $this->parts['path'] = '/' . $this->parts['path'];
207 2
        }
208
209 387
        $this->authority = $this->buildAuthority();
210 387
        $this->segments = $this->buildSegments();
211 387
        $this->primaryIdentifier = $this->buildPrimaryIdentifier();
212 387
    }
213
214 387
    private function buildAuthority()
215
    {
216 387
        $userInfo = $this->parts['user'];
217 387
        $authority = $this->parts['host'];
218
219 387
        if ($this->parts['pass'] !== '') {
220 1
            $userInfo .= ':'.$this->parts['pass'];
221 1
        }
222
223 387
        if ($this->parts['port'] !== '') {
224 14
            $authority .= ':'.$this->parts['port'];
225 14
        }
226
227 387
        if ($userInfo !== '') {
228 1
            $authority = $userInfo.'@'.$authority;
229 1
        }
230
231 387
        return $authority;
232
    }
233
234 387
    private function buildSegments()
235
    {
236 387
        $segments = [];
237
238 387
        if (substr($this->parts['fragment'], 0, 1) === '/') {
239 68
            $rawSegments = explode('/', substr($this->parts['fragment'], 1));
240
241 68
            foreach ($rawSegments as $segment) {
242 68
                $segment = str_replace('~1', '/', $segment);
243 68
                $segment = str_replace('~0', '~', $segment);
244 68
                $segments[] = $segment;
245 68
            }
246 68
        }
247
248 387
        return $segments;
249
    }
250
251 387
    private function buildPrimaryIdentifier()
252
    {
253 387
        $identifier = '';
254
255 387
        if ($this->parts['scheme'] !== '') {
256 366
            $identifier .= $this->parts['scheme'].'://';
257 366
        }
258
259 387
        $identifier .= $this->authority.$this->parts['path'];
260
261 387
        if ($this->parts['query'] !== '') {
262 6
            $identifier .= '?'.$this->parts['query'];
263 6
        }
264
265 387
        if ($this->parts['fragment'] !== '' && $this->parts['fragment'][0] !== '/') {
266 7
            $identifier .= '#'.$this->parts['fragment'];
267 7
        }
268
269 387
        return $identifier;
270
    }
271
272 59
    private function buildResolvedUriAgainst(Uri $uri)
273
    {
274 59
        $scheme = $uri->getScheme();
275 59
        $authority = $uri->getAuthority();
276 59
        $path = $uri->getPath();
277 59
        $query = $uri->getQuery();
278
279 59
        if ($this->getAuthority()) {
280 3
            $authority = $this->getAuthority();
281 3
            $path = $this->getPath();
282 3
            $query = $this->getQuery();
283 59
        } elseif ($this->getPath()) {
284 14
            $path = $this->buildResolvedPathAgainst($uri->getPath());
285 14
            $query = $this->getQuery();
286 56
        } elseif ($this->getQuery()) {
287 1
            $query = $this->getQuery();
288 1
        }
289
290 59
        return $this->appendRelativeParts(
291 59
            "{$scheme}://{$authority}{$path}",
292 59
            $query,
293 59
            $this->parts['fragment']
294 59
        );
295
    }
296
297 14
    private function buildResolvedPathAgainst($againstPath)
298
    {
299 14
        $ownPath = $this->getPath();
300
301 14
        if (0 !== strpos($ownPath, '/')) {
302 10
            $againstPath = $againstPath ?: '/';
303
304 10
            return preg_replace('#/([^/]*)$#', "/{$ownPath}", $againstPath);
305
        }
306
307 5
        return $ownPath;
308
    }
309
310 59
    private function appendRelativeParts($absolutePart, $query, $fragment)
311
    {
312 59
        if ($query) {
313 3
            $absolutePart .= '?'.$query;
314 3
        }
315
316 59
        if ($fragment) {
317 46
            $absolutePart .= '#'.$fragment;
318 46
        }
319
320 59
        return $absolutePart;
321
    }
322
}
323