Passed
Push — master ( 35c885...2ba443 )
by Filipe
01:25
created

Uri::getHost()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of slick/http
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Slick\Http\Message;
13
14
use Psr\Http\Message\UriInterface;
15
use Slick\Http\Message\Exception\InvalidArgumentException;
16
17
/**
18
 * Uri
19
 *
20
 * @package Slick\Http\Message
21
*/
22
class Uri implements UriInterface
23
{
24
    /** @var array<string, int>|int[]  */
25
    private array $defaultPorts = [
26
        'http' => 80,
27
        'https' => 443
28
    ];
29
30
    /**
31
     * @var string
32
     */
33
    private string $scheme = 'https';
34
35
    /**
36
     * @var string
37
     */
38
    private string $host = 'localhost';
39
40
    /**
41
     * @var string
42
     */
43
    private int|string $port = '';
44
45
    /**
46
     * @var string
47
     */
48
    private string $user = '';
49
50
    /**
51
     * @var string
52
     */
53
    private string $pass = '';
54
55
    /**
56
     * @var string
57
     */
58
    private string $path = '';
59
60
    /**
61
     * @var string
62
     */
63
    private string $query = '';
64
65
    /**
66
     * @var string
67
     */
68
    private string $fragment = '';
69
70
    /**
71
     * Creates an URI
72
     *
73
     * @param string $url
74
     *
75
     * @throws InvalidArgumentException if provided URL is not a valid URL
76
     *      according to PHP FILTER_VALIDATE_URL
77
     * @see http://php.net/manual/en/filter.filters.validate.php
78
     */
79
    public function __construct(string $url)
80
    {
81
        if (!filter_var($url, FILTER_VALIDATE_URL)) {
82
            throw new InvalidArgumentException(
83
                "The URL entered is invalid. URI cannot be created."
84
            );
85
        }
86
87
        /** @var array{scheme?: string, host?: string, port?: int<0, 65535>, user?: string, pass?: string,
88
         *     path?: string, query?: string, fragment?: string}|false $parsedUrl */
89
        $parsedUrl = parse_url($url);
90
91
        if (!\is_array($parsedUrl) || empty($parsedUrl)) {
0 ignored issues
show
introduced by
The condition is_array($parsedUrl) is always true.
Loading history...
92
            throw new InvalidArgumentException(
93
                "The URL entered is invalid. URI cannot be created."
94
            );
95
        }
96
97
        foreach ($parsedUrl as $property => $value) {
98
            if (property_exists($this, $property)) {
99
                // Normaliza todos os valores para string antes de atribuir
100
                $this->$property = (string) $value;
101
            }
102
        }
103
104
        foreach ($parsedUrl as $property => $value) {
105
            $this->$property = $value;
106
        }
107
    }
108
109
    /**
110
     * Retrieve the scheme component of the URI.
111
     *
112
     * @see https://tools.ietf.org/html/rfc3986#section-3.1
113
     * @return string The URI scheme.
114
     */
115
    public function getScheme(): string
116
    {
117
        return strtolower($this->scheme);
118
    }
119
120
    /**
121
     * Retrieve the authority component of the URI.
122
     *
123
     * @return string The URI authority, in "[user-info@]host[:port]" format.
124
     */
125
    public function getAuthority(): string
126
    {
127
        $authority = $this->getUserInfo() !== ''
128
            ? "{$this->getUserInfo()}@{$this->getHost()}"
129
            : "{$this->getHost()}";
130
        return $this->getPort() ? "$authority:{$this->getPort()}" : $authority;
131
    }
132
133
    /**
134
     * Retrieve the user information component of the URI.
135
     *
136
     * @return string The URI user information, in "username[:password]" format.
137
     */
138
    public function getUserInfo(): string
139
    {
140
        $userInfo = $this->user ?: '';
141
        return $this->pass ? "$userInfo:$this->pass" : $userInfo;
142
    }
143
144
    /**
145
     * Retrieve the host component of the URI.
146
     *
147
     * @see http://tools.ietf.org/html/rfc3986#section-3.2.2
148
     * @return string The URI host.
149
     */
150
    public function getHost(): string
151
    {
152
        return strtolower($this->host);
153
    }
154
155
    /**
156
     * Retrieve the port component of the URI.
157
     *
158
     * @return null|int The URI port.
159
     */
160
    public function getPort(): ?int
161
    {
162
        $default = \array_key_exists($this->getScheme(), $this->defaultPorts)
163
            ? $this->defaultPorts[$this->getScheme()]
164
            : -1;
165
166
        if ((string) $default === (string) $this->port) {
167
            return null;
168
        }
169
170
        return (int) $this->port;
171
    }
172
173
    /**
174
     * Retrieve the path component of the URI.
175
     *
176
     * @see https://tools.ietf.org/html/rfc3986#section-2
177
     * @see https://tools.ietf.org/html/rfc3986#section-3.3
178
     * @return string The URI path.
179
     */
180
    public function getPath(): string
181
    {
182
        return $this->path;
183
    }
184
185
    /**
186
     * Retrieve the query string of the URI.
187
     *
188
     * @see https://tools.ietf.org/html/rfc3986#section-2
189
     * @see https://tools.ietf.org/html/rfc3986#section-3.4
190
     * @return string The URI query string.
191
     */
192
    public function getQuery(): string
193
    {
194
        return $this->query;
195
    }
196
197
    /**
198
     * Retrieve the fragment component of the URI.
199
     *
200
     * @see https://tools.ietf.org/html/rfc3986#section-2
201
     * @see https://tools.ietf.org/html/rfc3986#section-3.5
202
     * @return string The URI fragment.
203
     */
204
    public function getFragment(): string
205
    {
206
        return $this->fragment;
207
    }
208
209
    /**
210
     * Return an instance with the specified scheme.
211
     *
212
     * @param string $scheme The scheme to use with the new instance.
213
     * @return static A new instance with the specified scheme.
214
     * @throws \InvalidArgumentException for invalid or unsupported schemes.
215
     */
216
    public function withScheme(string $scheme): UriInterface
217
    {
218
        $this->validateCharacters($scheme);
219
220
        $uri = clone $this;
221
        $uri->scheme = $scheme;
222
        return $uri;
223
    }
224
225
    /**
226
     * Return an instance with the specified user information.
227
     *
228
     * @param string $user The user name to use for authority.
229
     * @param string|null $password The password associated with $user.
230
     * @return static A new instance with the specified user information.
231
     */
232
    public function withUserInfo(string $user, ?string $password = null): UriInterface
233
    {
234
        $uri = clone $this;
235
        $uri->user = $user;
236
        $uri->pass = (string) $password;
237
        return $uri;
238
    }
239
240
    /**
241
     * Return an instance with the specified host.
242
     *
243
     * @param string $host The hostname to use with the new instance.
244
     * @return static A new instance with the specified host.
245
     * @throws \InvalidArgumentException for invalid hostnames.
246
     */
247
    public function withHost(string $host): UriInterface
248
    {
249
        $this->validateCharacters($host);
250
251
        $uri = clone $this;
252
        $uri->host = $host;
253
        return $uri;
254
    }
255
256
    /**
257
     * Return an instance with the specified port.
258
     *
259
     * @param int|null $port The port to use with the new instance; a null value
260
     *     removes the port information.
261
     * @return static A new instance with the specified port.
262
     * @throws \InvalidArgumentException for invalid ports.
263
     */
264
    public function withPort(?int $port): UriInterface
265
    {
266
        $uri = clone $this;
267
        $uri->port = (int) $port;
268
        return $uri;
269
    }
270
271
    /**
272
     * Return an instance with the specified path.
273
     *
274
     * @param string $path The path to use with the new instance.
275
     * @return static A new instance with the specified path.
276
     * @throws \InvalidArgumentException for invalid paths.
277
     */
278
    public function withPath(string $path): UriInterface
279
    {
280
        $uri = clone $this;
281
        $uri->path = $path;
282
        return $uri;
283
    }
284
285
    /**
286
     * Return an instance with the specified query string.
287
     *
288
     * An empty query string value is equivalent to removing the query string.
289
     *
290
     * @param string $query The query string to use with the new instance.
291
     * @return static A new instance with the specified query string.
292
     * @throws \InvalidArgumentException for invalid query strings.
293
     */
294
    public function withQuery(string $query): UriInterface
295
    {
296
        $uri = clone $this;
297
        $uri->query = $query;
298
        return $uri;
299
    }
300
301
    /**
302
     * Return an instance with the specified URI fragment.
303
     *
304
     * An empty fragment value is equivalent to removing the fragment.
305
     *
306
     * @param string $fragment The fragment to use with the new instance.
307
     * @return static A new instance with the specified fragment.
308
     */
309
    public function withFragment(string $fragment): UriInterface
310
    {
311
        $uri = clone $this;
312
        $uri->fragment = $fragment;
313
        return $uri;
314
    }
315
316
    /**
317
     * Return the string representation as a URI reference.
318
     *
319
     * @see http://tools.ietf.org/html/rfc3986#section-4.1
320
     * @return string
321
     */
322
    public function __toString(): string
323
    {
324
        $text  = $this->scheme ? "{$this->getScheme()}:" : '';
325
        $text .= $this->getAuthority() !== ''
326
            ? "//{$this->getAuthority()}"
327
            : '';
328
        $text .= '/'. ltrim($this->getPath(), '/');
329
        $text .= \strlen($this->query) > 0 ? "?$this->query" : '';
330
        $text .= \strlen($this->fragment) > 0 ? "#$this->fragment" : '';
331
        return $text;
332
    }
333
334
    /**
335
     * Check the provided text for invalid characters
336
     *
337
     * @param string $text
338
     */
339
    private function validateCharacters(string $text): void
340
    {
341
        $regex = '/^[a-z][a-z0-9.+\-]*/i';
342
        if ($text !== '' && ! preg_match($regex, $text)) {
343
            throw new InvalidArgumentException(
344
                "Invalid characters used in URI."
345
            );
346
        }
347
    }
348
}
349