Issues (41)

src/Message/Uri.php (11 issues)

1
<?php
2
3
namespace DMT\Aura\Psr\Message;
4
5
use Aura\Web\Exception\InvalidComponent;
6
use Aura\Web\Request\Url as AuraUrl;
7
use DMT\Aura\Psr\Helpers\HelperFactory;
8
use InvalidArgumentException;
9
use Psr\Http\Message\UriInterface;
10
11
/**
12
 * Class Uri
13
 *
14
 * @package DMT\Aura\Psr\Message
15
 */
16
class Uri implements UriInterface
17
{
18
    /** Url components */
19
    const URI_COMPONENTS = [
20
        PHP_URL_SCHEME => 'scheme',
21
        PHP_URL_HOST => 'host',
22
        PHP_URL_PORT => 'port',
23
        PHP_URL_USER => 'user',
24
        PHP_URL_PASS => 'pass',
25
        PHP_URL_PATH => 'path',
26
        PHP_URL_QUERY => 'query',
27
        PHP_URL_FRAGMENT => 'fragment',
28
    ];
29
30
    /** Default ports for schemes. */
31
    const DEFAULT_PORTS = [
32
        'http' => 80,
33
        'https' => 443,
34
    ];
35
36
    /** @var AuraUrl $object */
37
    protected $object;
38
39
    /**
40
     * Uri constructor.
41
     *
42
     * @param string|null $uri
43
     */
44 71
    public function __construct(string $uri = null)
45
    {
46 71
        $object = $this->getInnerObject();
47
48 71
        if ($uri) {
49 70
            (new HelperFactory())
50 70
                ->createHelper($object)
51 70
                ->setObjectProperty('parts', parse_url($uri));
52
        }
53 71
    }
54
55
    /**
56
     * @return AuraUrl
57
     */
58 71
    public function getInnerObject(): AuraUrl
59
    {
60 71
        if (!$this->object) {
61 71
            $this->object = new AuraUrl([]);
62
        }
63 71
        return $this->object;
64
    }
65
66
67
    /**
68
     * Retrieve the scheme component of the URI.
69
     *
70
     * @return string
71
     */
72 16
    public function getScheme(): string
73
    {
74 16
        return rtrim($this->urlGet(PHP_URL_SCHEME), ' :/');
75
    }
76
77
    /**
78
     * Retrieve the authority component of the URI.
79
     *
80
     * @return string
81
     */
82 12
    public function getAuthority(): string
83
    {
84 12
        $authority = '';
85
86 12
        $host = $this->getHost();
87 12
        if ($host !== '') {
88 11
            $userInfo = $this->getUserInfo();
89 11
            if ($userInfo !== '') {
90 4
                $authority .= $userInfo . '@';
91
            }
92
93 11
            $authority .= $host;
94
95 11
            $port = $this->getPort();
96 11
            if ($port !== null) {
97 3
                $authority .= ':' . $port;
98
            }
99
        }
100
101 12
        return $authority;
102
    }
103
104
    /**
105
     * Retrieve the user information component of the URI.
106
     *
107
     * @return string
108
     */
109 12
    public function getUserInfo(): string
110
    {
111 12
        $user = $this->urlGet(PHP_URL_USER);
112 12
        $pass = $this->urlGet(PHP_URL_PASS);
113
114 12
        $userInfo = '';
115 12
        if ($user !== '') {
116 5
            $userInfo .= $user;
117 5
            if ($pass !== '') {
118 4
                $userInfo .= ':' . $pass;
119
            }
120
        }
121
122
123 12
        return $userInfo;
124
    }
125
126
    /**
127
     * Retrieve the host component of the URI.
128
     *
129
     * @return string
130
     */
131 16
    public function getHost(): string
132
    {
133 16
        return strtolower($this->urlGet(PHP_URL_HOST));
134
    }
135
136
    /**
137
     * Retrieve the port component of the URI.
138
     *
139
     * @return null|int
140
     */
141 15
    public function getPort()
142
    {
143 15
        $port = $this->urlGet(PHP_URL_PORT, null);
144 15
        if ($port == (self::DEFAULT_PORTS[$this->getScheme()] ?? null)) {
145 5
            $port = null;
146
        }
147
148 15
        return $port > 0 ? (int)$port : null;
149
    }
150
151
    /**
152
     * Retrieve the path component of the URI.
153
     *
154
     * @return string
155
     */
156 17
    public function getPath(): string
157
    {
158 17
        $path = $this->urlGet(PHP_URL_PATH);
159 17
        if (empty($path)) {
160 4
            return '';
161
        }
162
163 14
        $paths = array_map([$this, 'decode'], explode('/', $path));
164
165 14
        return implode('/', array_map('rawurlencode', $paths));
166
    }
167
168
    /**
169
     * Retrieve the query string of the URI.
170
     *
171
     * @return string
172
     */
173 16
    public function getQuery(): string
174
    {
175 16
        $query = $this->urlGet(PHP_URL_QUERY);
176 16
        if ($query === '') {
177 10
            return '';
178
        }
179
180 7
        parse_str($query, $params);
181
182 7
        $params = array_map([$this, 'decode'], $params);
183
184 7
        return preg_replace('~=(&|$)~', "$1", http_build_query($params, '', '&', PHP_QUERY_RFC3986));
185
    }
186
187
    /**
188
     * Retrieve the fragment component of the URI.
189
     *
190
     * @return string
191
     */
192 15
    public function getFragment(): string
193
    {
194 15
        return rawurlencode($this->decode($this->urlGet(PHP_URL_FRAGMENT)));
195
    }
196
197
    /**
198
     * Return an instance with the specified scheme.
199
     *
200
     * @param string $scheme
201
     * @return static
202
     * @throws InvalidArgumentException
203
     */
204 6
    public function withScheme($scheme): self
205
    {
206 6
        if (!is_string($scheme) || !preg_match('~[a-z]~i', $scheme)) {
0 ignored issues
show
The condition is_string($scheme) is always true.
Loading history...
207 4
            throw new InvalidArgumentException('invalid scheme given');
208
        }
209
210 2
        return  $this->urlSet(PHP_URL_SCHEME, strtolower($scheme) . '://');
211
    }
212
213
    /**
214
     * Return an instance with the specified user information.
215
     *
216
     * @param string $user
217
     * @param null|string $password
218
     * @return static
219
     */
220 1
    public function withUserInfo($user, $password = null): self
221
    {
222 1
        if (!is_string($user) || $user === '') {
0 ignored issues
show
The condition is_string($user) is always true.
Loading history...
223
            throw new InvalidArgumentException('invalid user given');
224
        }
225
226 1
        if (!is_string($password) && !is_null($password)) {
0 ignored issues
show
The condition is_null($password) is always true.
Loading history...
227
            throw new InvalidArgumentException('invalid password given');
228
        }
229
230
        return $this
231 1
            ->urlSet(PHP_URL_USER, $user)
232 1
            ->urlSet(PHP_URL_PASS, $password);
0 ignored issues
show
It seems like $password can also be of type null; however, parameter $value of DMT\Aura\Psr\Message\Uri::urlSet() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

232
            ->urlSet(PHP_URL_PASS, /** @scrutinizer ignore-type */ $password);
Loading history...
233
    }
234
235
    /**
236
     * Return an instance with the specified host.
237
     *
238
     * @param string $host
239
     * @return static
240
     * @throws InvalidArgumentException
241
     */
242 1
    public function withHost($host): self
243
    {
244 1
        if (!is_string($host)) {
0 ignored issues
show
The condition is_string($host) is always true.
Loading history...
245
            throw new InvalidArgumentException('invalid host given');
246
        }
247
248 1
        return $this->urlSet(PHP_URL_HOST, $host);
249
    }
250
251
    /**
252
     * Return an instance with the specified port.
253
     *
254
     * @param null|int $port
255
     * @return static
256
     * @throws InvalidArgumentException
257
     */
258 1
    public function withPort($port): self
259
    {
260 1
        if (is_null($port)) {
261
            return $this->urlSet('port', '');
0 ignored issues
show
'port' of type string is incompatible with the type integer expected by parameter $component of DMT\Aura\Psr\Message\Uri::urlSet(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

261
            return $this->urlSet(/** @scrutinizer ignore-type */ 'port', '');
Loading history...
262
        }
263
264 1
        if (!is_int($port) || $port < 1) {
0 ignored issues
show
The condition is_int($port) is always true.
Loading history...
265
            throw new InvalidArgumentException('invalid post given');
266
        }
267
268 1
        return $this->urlSet(PHP_URL_PORT, $port);
269
    }
270
271
    /**
272
     * Return an instance with the specified path.
273
     *
274
     * @param string $path
275
     * @return static
276
     * @throws InvalidArgumentException
277
     */
278 1
    public function withPath($path): self
279
    {
280 1
        if (!is_string($path)) {
0 ignored issues
show
The condition is_string($path) is always true.
Loading history...
281
            throw new InvalidArgumentException('invalid path given');
282
        }
283
284 1
        return $this->urlSet(PHP_URL_PATH, $path);
285
    }
286
287
    /**
288
     * Return an instance with the specified query string.
289
     *
290
     * @param string $query
291
     * @return static
292
     * @throws InvalidArgumentException
293
     */
294 1
    public function withQuery($query): self
295
    {
296 1
        if (!is_string($query)) {
0 ignored issues
show
The condition is_string($query) is always true.
Loading history...
297
            throw new InvalidArgumentException('invalid path given');
298
        }
299
300 1
        return $this->urlSet(PHP_URL_QUERY, $query);
301
    }
302
303
    /**
304
     * Return an instance with the specified URI fragment.
305
     *
306
     * @param string $fragment
307
     * @return static
308
     */
309 1
    public function withFragment($fragment): self
310
    {
311 1
        if (!is_string($fragment)) {
0 ignored issues
show
The condition is_string($fragment) is always true.
Loading history...
312
            throw new InvalidArgumentException('invalid fragment given');
313
        }
314
315 1
        return $this->urlSet(PHP_URL_FRAGMENT, $fragment);
316
    }
317
318
    /**
319
     * Return the string representation as a URI reference.
320
     *
321
     * @return string
322
     */
323 11
    public function __toString(): string
324
    {
325 11
        $scheme = $this->getScheme();
326 11
        $authority = $this->getAuthority();
327 11
        $path = $this->getPath();
328 11
        $query = $this->getQuery();
329 11
        $fragment = $this->getFragment();
330
331 11
        $uri = '';
332 11
        if ($scheme !== '') {
333 10
            $uri .= $scheme . ':';
334
        }
335
336 11
        if ($authority !== '') {
337 10
            $uri .= '//' . $authority;
338
        }
339
340 11
        $uri .= '/' . ltrim($path, ' /');
341
342 11
        if ($query !== '') {
343 4
            $uri .= '?' . $query;
344
        }
345
346 11
        if ($fragment !== '') {
347 3
            $uri .= '#' . $fragment;
348
        }
349
350 11
        return $uri;
351
    }
352
353
    /**
354
     * Decode a value.
355
     *
356
     * @param array|string $data
357
     * @return array|string
358
     */
359 23
    protected function decode($data)
360
    {
361 23
        if (is_array($data)) {
362
            return array_map([$this, __FUNCTION__], $data);
363
        }
364 23
        return rawurldecode((string)$data);
365
    }
366
367
    /**
368
     * Get a url part.
369
     *
370
     * @param int $component
371
     * @param string|null $default
372
     * @return string|null
373
     */
374 33
    private function urlGet(int $component, $default = '')
375
    {
376
        try {
377 33
            return $this->object->get($component) ?? $default;
378
        } catch (InvalidComponent $exception) {
379
            return $default;
380
        }
381
    }
382
383
    /**
384
     * Store a url part.
385
     *
386
     * @param string|int $component
387
     * @param string|null $value
388
     * @return static
389
     */
390 2
    private function urlSet(int $component, string $value): self
391
    {
392 2
        $component = [self::URI_COMPONENTS[$component] => $value];
393
394 2
        $instance = clone($this);
395
396 2
        $helper = (new HelperFactory())->createHelper($instance->getInnerObject());
397 2
        $helper->setObjectProperty('parts', $component + $helper->getObjectProperty('parts', []));
398 2
        $helper->setObjectProperty('string', (string)$instance);
399 2
        if ($component === PHP_URL_SCHEME) {
0 ignored issues
show
The condition $component === DMT\Aura\Psr\Message\PHP_URL_SCHEME is always false.
Loading history...
400
            $helper->getObjectProperty('secure', stripos($value, 'https') === 0);
401
        }
402
403 2
        return $instance;
404
    }
405
}
406