Uri::withUserInfo()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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