Uri::getRawPointer()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 0
crap 2
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 382
    public function __construct($rawUri)
60
    {
61 382
        $this->buildFromRawUri($rawUri);
62 381
    }
63
64
    /**
65
     * @return string
66
     */
67 3
    public function getRawUri()
68
    {
69 3
        return $this->raw;
70
    }
71
72
    /**
73
     * @return string
74
     */
75 55
    public function getRawPointer()
76
    {
77 55
        return isset($this->parts['fragment']) ? $this->parts['fragment'] : '';
78
    }
79
80
    /**
81
     * @return bool
82
     */
83 90
    public function isAbsolute()
84
    {
85 90
        return $this->parts['scheme'] !== '';
86
    }
87
88
    /**
89
     * @return string
90
     */
91 63
    public function getScheme()
92
    {
93 63
        return $this->parts['scheme'];
94
    }
95
96
    /**
97
     * @return string
98
     */
99 63
    public function getAuthority()
100
    {
101 63
        return $this->authority;
102
    }
103
104
    /**
105
     * @return string
106
     */
107 63
    public function getPath()
108
    {
109 63
        return $this->parts['path'];
110
    }
111
112
    /**
113
     * @return string
114
     */
115 63
    public function getQuery()
116
    {
117 63
        return $this->parts['query'];
118
    }
119
120
    /**
121
     * @return string[]
122
     */
123 65
    public function getPointerSegments()
124
    {
125 65
        return $this->segments;
126
    }
127
128
    /**
129
     * Returns the primary resource identifier part of the URI, i.e. everything
130
     * excluding its fragment part.
131
     *
132
     * @return string
133
     */
134 350
    public function getPrimaryResourceIdentifier()
135
    {
136 350
        return $this->primaryIdentifier;
137
    }
138
139
    /**
140
     * Resolves the current (relative) URI against another (absolute) URI.
141
     * Example:.
142
     *
143
     * Current  = foo.json
144
     * Other    = http://localhost/bar/baz
145
     * Resolved = http://localhost/bar/foo.json
146
     *
147
     * @param Uri $uri
148
     *
149
     * @return string
150
     */
151 65
    public function resolveAgainst(Uri $uri)
152
    {
153 65
        if ($this->isAbsolute()) {
154 1
            throw new \LogicException(
155
                'Cannot resolve against another URI: URI is already absolute'
156 1
            );
157
        }
158
159 64
        if (!$uri->isAbsolute()) {
160 1
            throw new \LogicException(
161
                'Cannot resolve against another URI: reference URI is not absolute'
162 1
            );
163
        }
164
165 63
        $resolved = $this->buildResolvedUriAgainst($uri);
166 63
        $this->buildFromRawUri($resolved);
167
168 63
        return $resolved;
169
    }
170
171
    /**
172
     * Returns whether two URIs share the same primary resource identifier,
173
     * i.e. whether they point to the same document.
174
     *
175
     * @param Uri $uri
176
     *
177
     * @return bool
178
     */
179 5
    public function isSamePrimaryResource(Uri $uri)
180
    {
181 5
        if (!$this->isAbsolute() || !$uri->isAbsolute()) {
182 1
            throw new \LogicException('Cannot compare URIs: both must be absolute');
183
        }
184
185 4
        return $this->primaryIdentifier === $uri->getPrimaryResourceIdentifier();
186
    }
187
188 382
    private function buildFromRawUri($rawUri)
189
    {
190 382
        $this->raw = rawurldecode($rawUri);
191 382
        $this->parts = @parse_url($this->raw);
192
193 382
        if (false === $this->parts) {
194 1
            throw new \InvalidArgumentException("Cannot parse URI '{$rawUri}'");
195
        }
196
197 381
        foreach (self::$partNames as $part) {
198 381
            if (!isset($this->parts[$part])) {
199 381
                $this->parts[$part] = '';
200 381
            }
201 381
        }
202
203 381
        if ($this->parts['scheme'] === 'file' && preg_match('/^[A-Z]:/i', $this->parts['path'])) {
204 2
            $this->parts['path'] = '/' . $this->parts['path'];
205 2
        }
206
207 381
        $this->authority = $this->buildAuthority();
208 381
        $this->segments = $this->buildSegments();
209 381
        $this->primaryIdentifier = $this->buildPrimaryIdentifier();
210 381
    }
211
212 381
    private function buildAuthority()
213
    {
214 381
        $userInfo = $this->parts['user'];
215 381
        $authority = $this->parts['host'];
216
217 381
        if ($this->parts['pass'] !== '') {
218 1
            $userInfo .= ':'.$this->parts['pass'];
219 1
        }
220
221 381
        if ($this->parts['port'] !== '') {
222 14
            $authority .= ':'.$this->parts['port'];
223 14
        }
224
225 381
        if ($userInfo !== '') {
226 1
            $authority = $userInfo.'@'.$authority;
227 1
        }
228
229 381
        return $authority;
230
    }
231
232 381
    private function buildSegments()
233
    {
234 381
        $segments = [];
235
236 381
        if (isset($this->parts['fragment'])) {
237 381
            $rawSegments = explode('/', $this->parts['fragment']);
238
239 381
            foreach ($rawSegments as $segment) {
240 381
                $segment = trim($segment);
241
242 381
                if ($segment !== '') {
243 61
                    $segment = str_replace('~1', '/', $segment);
244 61
                    $segment = str_replace('~0', '~', $segment);
245 61
                    $segments[] = $segment;
246 61
                }
247 381
            }
248 381
        }
249
250 381
        return $segments;
251
    }
252
253 381
    private function buildPrimaryIdentifier()
254
    {
255 381
        $identifier = '';
256
257 381
        if ($this->parts['scheme']) {
258 372
            $identifier .= $this->parts['scheme'].'://';
259 372
        }
260
261 381
        $identifier .= $this->authority.$this->parts['path'];
262
263 381
        if ($this->parts['query']) {
264 6
            $identifier .= '?'.$this->parts['query'];
265 6
        }
266
267 381
        return $identifier;
268
    }
269
270 63
    private function buildResolvedUriAgainst(Uri $uri)
271
    {
272 63
        $scheme = $uri->getScheme();
273 63
        $authority = $uri->getAuthority();
274 63
        $path = $uri->getPath();
275 63
        $query = $uri->getQuery();
276
277 63
        if ($this->getAuthority()) {
278 3
            $authority = $this->getAuthority();
279 3
            $path = $this->getPath();
280 3
            $query = $this->getQuery();
281 63
        } elseif ($this->getPath()) {
282 14
            $path = $this->buildResolvedPathAgainst($uri->getPath());
283 14
            $query = $this->getQuery();
284 60
        } elseif ($this->getQuery()) {
285 1
            $query = $this->getQuery();
286 1
        }
287
288 63
        return $this->appendRelativeParts(
289 63
            "{$scheme}://{$authority}{$path}",
290 63
            $query,
291 63
            $this->parts['fragment']
292 63
        );
293
    }
294
295 14
    private function buildResolvedPathAgainst($againstPath)
296
    {
297 14
        $ownPath = $this->getPath();
298
299 14
        if (0 !== strpos($ownPath, '/')) {
300 10
            $againstPath = $againstPath ?: '/';
301
302 10
            return preg_replace('#/([^/]*)$#', "/{$ownPath}", $againstPath);
303
        }
304
305 5
        return $ownPath;
306
    }
307
308 63
    private function appendRelativeParts($absolutePart, $query, $fragment)
309
    {
310 63
        if ($query) {
311 3
            $absolutePart .= '?'.$query;
312 3
        }
313
314 63
        if ($fragment) {
315 50
            $absolutePart .= '#'.$fragment;
316 50
        }
317
318 63
        return $absolutePart;
319
    }
320
}
321