Uri::filterPath()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 1
1
<?php
2
3
namespace Resilient\Http;
4
5
use InvalidArgumentException;
6
use \Psr\Http\Message\UriInterface;
7
8
class Uri implements UriInterface
9
{
10
    /**
11
     * Uri scheme (without "://" suffix)
12
     *
13
     * @var string
14
     */
15
    protected $scheme = '';
16
17
    /**
18
     * Uri user
19
     *
20
     * @var string
21
     */
22
    protected $user = '';
23
24
    /**
25
     * Uri password
26
     *
27
     * @var string
28
     */
29
    protected $password = '';
30
31
    /**
32
     * Uri host
33
     *
34
     * @var string
35
     */
36
    protected $host = '';
37
38
    /**
39
     * Uri port number
40
     *
41
     * @var null|int
42
     */
43
    protected $port;
44
45
    /**
46
     * Uri path
47
     *
48
     * @var string
49
     */
50
    protected $path = '';
51
52
    /**
53
     * Uri query string (without "?" prefix)
54
     *
55
     * @var string
56
     */
57
    protected $query = '';
58
59
    /**
60
     * Uri fragment string (without "#" prefix)
61
     *
62
     * @var string
63
     */
64
    protected $fragment = '';
65
66
    /**
67
     * Instance new Uri.
68
     *
69
     * @param string $scheme   Uri scheme.
70
     * @param string $host     Uri host.
71
     * @param int    $port     Uri port number.
72
     * @param string $path     Uri path.
73
     * @param string $query    Uri query string.
74
     * @param string $fragment Uri fragment.
75
     * @param string $user     Uri user.
76
     * @param string $password Uri password.
77
     */
78
    public function __construct (
79
        $scheme,
80
        $host,
81
        $port = null,
82
        $path = '/',
83
        $query = '',
84
        $fragment = '',
85
        $user = '',
86
        $password = ''
87
    ) {
88
        $this->scheme = $this->filterScheme($scheme);
89
        $this->host = $host;
90
        $this->port = $this->filterPort($port);
91
        $this->path = empty($path) ? '/' : $this->filterPath($path);
92
        $this->query = $this->filterQuery($query);
93
        $this->fragment = $this->filterQuery($fragment);
94
        $this->user = $user;
95
        $this->password = $password;
96
    }
97
98
    /**
99
     * {@inheritdoc}
100
     */
101
    public function getScheme ()
102
    {
103
        return $this->scheme;
104
    }
105
106
    /**
107
     * {@inheritdoc}
108
     */
109
    public function getAuthority ()
110
    {
111
        $userInfo = $this->getUserInfo();
112
        $host = $this->getHost();
113
        $port = $this->getPort();
114
115
        return ($userInfo ? $userInfo . '@' : '') . $host . ($port !== null ? ':' . $port : '');
116
    }
117
118
    /**
119
     * {@inheritdoc}
120
     */
121
    public function getUserInfo ()
122
    {
123
        return $this->user . ($this->password ? ':' . $this->password : '');
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function getHost ()
130
    {
131
        return $this->host;
132
    }
133
134
    /**
135
     * {@inheritdoc}
136
     */
137
    public function getPort ()
138
    {
139
        return $this->port !== null && !$this->hasStandardPort() ? $this->port : null;
140
    }
141
142
    /**
143
     * {@inheritdoc}
144
     */
145
    public function getPath ()
146
    {
147
        return $this->path;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153
    public function getQuery ()
154
    {
155
        return $this->query;
156
    }
157
158
    /**
159
     * {@inheritdoc}
160
     */
161
    public function getFragment ()
162
    {
163
        return $this->fragment;
164
    }
165
166
    /**
167
     * {@inheritdoc}
168
     */
169
    public function withScheme ($scheme)
170
    {
171
        $scheme = $this->filterScheme($scheme);
172
        $clone = clone $this;
173
        $clone->scheme = $scheme;
174
175
        return $clone;
176
    }
177
178
    /**
179
     * {@inheritdoc}
180
     */
181
    public function withUserInfo ($user, $password = null)
182
    {
183
        $clone = clone $this;
184
        $clone->user = $user;
185
        $clone->password = $password ? $password : '';
186
187
        return $clone;
188
    }
189
190
    /**
191
     * {@inheritdoc}
192
     */
193
    public function withHost ($host)
194
    {
195
        $clone = clone $this;
196
        $clone->host = $host;
197
198
        return $clone;
199
    }
200
201
    /**
202
     * {@inheritdoc}
203
     */
204
    public function withPort ($port)
205
    {
206
        $port = $this->filterPort($port);
207
        $clone = clone $this;
208
        $clone->port = $port;
209
210
        return $clone;
211
    }
212
213
    /**
214
     * {@inheritdoc}
215
     */
216
    public function withPath ($path)
217
    {
218
        $clone = clone $this;
219
        $clone->path = $this->filterPath($path);
220
221
        return $clone;
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227
    public function withQuery ($query)
228
    {
229
        return $this->withString($query);
230
    }
231
232
    /**
233
     * {@inheritdoc}
234
     */
235
    public function withFragment ($fragment)
236
    {
237
        return $this->withString($fragment, 'fragment');
238
    }
239
240
    /**
241
     * withString function.
242
     *
243
     * @access protected
244
     * @param string $string
245
     * @param string $name (default: 'query')
246
     * @return Uri
247
     */
248
    protected function withString ($string, $name = 'query')
249
    {
250
        if (!is_string($string) && !method_exists($string, '__toString')) {
251
            throw new InvalidArgumentException('Uri fragment must be a string');
252
        }
253
        $string = ltrim((string) $string, '#');
254
        $clone = clone $this;
255
        $clone->$name = $this->filterQuery($string);
256
257
        return $clone;
258
    }
259
260
    /**
261
     * {@inheritdoc}
262
     */
263
    public function __toString ()
264
    {
265
        $scheme = $this->getScheme();
266
        $authority = $this->getAuthority();
267
        $path = $this->getPath();
268
        $query = $this->getQuery();
269
        $fragment = $this->getFragment();
270
271
        return ($scheme ? $scheme . ':' : '')
272
            . ($authority ? '//' . $authority : '')
273
            . $path
274
            . ($query ? '?' . $query : '')
275
            . ($fragment ? '#' . $fragment : '');
276
    }
277
278
    /*
279
        END OF UriInterface Implementation
280
    */
281
282
    /**
283
     * filter scheme given to only allow certain scheme, no file:// or ftp:// or other scheme because its http message uri interface
284
     *
285
     * @access protected
286
     * @param string $scheme
287
     * @return string $scheme
288
     * @throws InvalidArgumentException if not corret scheme is present
289
     */
290
    protected function filterScheme (string $scheme)
291
    {
292
        static $valid = [
293
            '' => true,
294
            'https' => true,
295
            'http' => true,
296
        ];
297
298
        $scheme = str_replace('://', '', strtolower($scheme));
299
        if (!isset($valid[$scheme])) {
300
            throw new InvalidArgumentException('Uri scheme must be one of: "", "https", "http"');
301
        }
302
303
        return $scheme;
304
    }
305
306
307
    /**
308
     * Filter allowable port to minimize risk
309
     *
310
     * @access protected
311
     * @param integer|null $port
312
     * @return null|integer $port
313
     * @throws InvalidArgumentException for incorrect port assigned
314
     */
315
    protected function filterPort ($port)
316
    {
317
        if ((integer) $port >= 0 && (integer) $port <= 65535) {
318
            return $port;
319
        }
320
321
        throw new InvalidArgumentException('Uri port must be null or an integer between 1 and 65535 (inclusive)');
322
    }
323
324
    /**
325
     * Path allowed chars filter, no weird path on uri yes?.
326
     *
327
     * @access protected
328
     * @param string $path
329
     * @return string of cleared path
330
     */
331
    protected function filterPath ($path)
332
    {
333
        return preg_replace_callback(
334
            '/(?:[^a-zA-Z0-9_\-\.~:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/',
335
            function($match) {
336
                return rawurlencode($match[0]);
337
            },
338
            $path
339
        );
340
    }
341
342
    /**
343
     * replace query to clear not allowed chars
344
     *
345
     * @access protected
346
     * @param string $query
347
     * @return string of replaced query
348
     */
349
    protected function filterQuery ($query)
350
    {
351
        return preg_replace_callback(
352
            '/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/',
353
            function($match) {
354
                return rawurlencode($match[0]);
355
            },
356
            $query
357
        );
358
    }
359
360
    /**
361
     * cek if current uri scheme use standard port
362
     *
363
     * @access protected
364
     * @return boolean
365
     */
366
    protected function hasStandardPort ()
367
    {
368
        return ($this->scheme === 'http' && $this->port === 80) || ($this->scheme === 'https' && $this->port === 443);
369
    }
370
371
    /**
372
     * get Base Url
373
     *
374
     * @access public
375
     * @return string
376
     */
377
    public function getBaseUrl ()
378
    {
379
        $scheme = $this->getScheme();
380
        $authority = $this->getAuthority();
381
382
        return ($scheme ? $scheme . ':' : '')
383
            . ($authority ? '//' . $authority : '');
384
    }
385
}
386