Issues (14)

src/Message/Uri.php (2 issues)

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