1<?php
2
3/**
4 * League.Uri (https://uri.thephpleague.com)
5 *
6 * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12declare(strict_types=1);
13
14namespace League\Uri;
15
16use JsonSerializable;
17use League\Uri\Contracts\UriInterface;
18use League\Uri\Exceptions\SyntaxError;
19use Psr\Http\Message\UriInterface as Psr7UriInterface;
20use function is_object;
21use function is_scalar;
22use function method_exists;
23use function sprintf;
24
25final class Http implements Psr7UriInterface, JsonSerializable
26{
27 private UriInterface $uri;
28
29 private function __construct(UriInterface $uri)
30 {
31 $this->validate($uri);
32 $this->uri = $uri;
33 }
34
35 /**
36 * Validate the submitted uri against PSR-7 UriInterface.
37 *
38 * @throws SyntaxError if the given URI does not follow PSR-7 UriInterface rules
39 */
40 private function validate(UriInterface $uri): void
41 {
42 $scheme = $uri->getScheme();
43 if (null === $scheme && '' === $uri->getHost()) {
44 throw new SyntaxError(sprintf('an URI without scheme can not contains a empty host string according to PSR-7: %s', (string) $uri));
45 }
46
47 $port = $uri->getPort();
48 if (null !== $port && ($port < 0 || $port > 65535)) {
49 throw new SyntaxError(sprintf('The URI port is outside the established TCP and UDP port ranges: %s', (string) $uri->getPort()));
50 }
51 }
52
53 /**
54 * Static method called by PHP's var export.
55 */
56 public static function __set_state(array $components): self
57 {
58 return new self($components['uri']);
59 }
60
61 /**
62 * Create a new instance from a string.
63 *
64 * @param string|mixed $uri
65 */
66 public static function createFromString($uri = ''): self
67 {
68 return new self(Uri::createFromString($uri));
69 }
70
71 /**
72 * Create a new instance from a hash of parse_url parts.
73 *
74 * @param array $components a hash representation of the URI similar
75 * to PHP parse_url function result
76 */
77 public static function createFromComponents(array $components): self
78 {
79 return new self(Uri::createFromComponents($components));
80 }
81
82 /**
83 * Create a new instance from the environment.
84 */
85 public static function createFromServer(array $server): self
86 {
87 return new self(Uri::createFromServer($server));
88 }
89
90 /**
91 * Create a new instance from a URI and a Base URI.
92 *
93 * The returned URI must be absolute.
94 *
95 * @param mixed $uri the input URI to create
96 * @param mixed $base_uri the base URI used for reference
97 */
98 public static function createFromBaseUri($uri, $base_uri = null): self
99 {
100 return new self(Uri::createFromBaseUri($uri, $base_uri));
101 }
102
103 /**
104 * Create a new instance from a URI object.
105 *
106 * @param Psr7UriInterface|UriInterface $uri the input URI to create
107 */
108 public static function createFromUri($uri): self
109 {
110 if ($uri instanceof UriInterface) {
111 return new self($uri);
112 }
113
114 return new self(Uri::createFromUri($uri));
115 }
116
117 /**
118 * {@inheritDoc}
119 */
120 public function getScheme(): string
121 {
122 return (string) $this->uri->getScheme();
123 }
124
125 /**
126 * {@inheritDoc}
127 */
128 public function getAuthority(): string
129 {
130 return (string) $this->uri->getAuthority();
131 }
132
133 /**
134 * {@inheritDoc}
135 */
136 public function getUserInfo(): string
137 {
138 return (string) $this->uri->getUserInfo();
139 }
140
141 /**
142 * {@inheritDoc}
143 */
144 public function getHost(): string
145 {
146 return (string) $this->uri->getHost();
147 }
148
149 /**
150 * {@inheritDoc}
151 */
152 public function getPort(): ?int
153 {
154 return $this->uri->getPort();
155 }
156
157 /**
158 * {@inheritDoc}
159 */
160 public function getPath(): string
161 {
162 return $this->uri->getPath();
163 }
164
165 /**
166 * {@inheritDoc}
167 */
168 public function getQuery(): string
169 {
170 return (string) $this->uri->getQuery();
171 }
172
173 /**
174 * {@inheritDoc}
175 */
176 public function getFragment(): string
177 {
178 return (string) $this->uri->getFragment();
179 }
180
181 /**
182 * {@inheritDoc}
183 */
184 public function withScheme($scheme): self
185 {
186 /** @var string $scheme */
187 $scheme = $this->filterInput($scheme);
188 if ('' === $scheme) {
189 $scheme = null;
190 }
191
192 $uri = $this->uri->withScheme($scheme);
193 if ($uri->getScheme() === $this->uri->getScheme()) {
194 return $this;
195 }
196
197 return new self($uri);
198 }
199
200 /**
201 * Safely stringify input when possible.
202 *
203 * @param mixed $str the value to evaluate as a string
204 *
205 * @throws SyntaxError if the submitted data can not be converted to string
206 *
207 * @return string|mixed
208 */
209 private function filterInput($str)
210 {
211 if (is_scalar($str) || (is_object($str) && method_exists($str, '__toString'))) {
212 return (string) $str;
213 }
214
215 return $str;
216 }
217
218 /**
219 * {@inheritDoc}
220 */
221 public function withUserInfo($user, $password = null): self
222 {
223 /** @var string $user */
224 $user = $this->filterInput($user);
225 if ('' === $user) {
226 $user = null;
227 }
228
229 $uri = $this->uri->withUserInfo($user, $password);
230 if ($uri->getUserInfo() === $this->uri->getUserInfo()) {
231 return $this;
232 }
233
234 return new self($uri);
235 }
236
237 /**
238 * {@inheritDoc}
239 */
240 public function withHost($host): self
241 {
242 /** @var string $host */
243 $host = $this->filterInput($host);
244 if ('' === $host) {
245 $host = null;
246 }
247
248 $uri = $this->uri->withHost($host);
249 if ($uri->getHost() === $this->uri->getHost()) {
250 return $this;
251 }
252
253 return new self($uri);
254 }
255
256 /**
257 * {@inheritDoc}
258 */
259 public function withPort($port): self
260 {
261 $uri = $this->uri->withPort($port);
262 if ($uri->getPort() === $this->uri->getPort()) {
263 return $this;
264 }
265
266 return new self($uri);
267 }
268
269 /**
270 * {@inheritDoc}
271 */
272 public function withPath($path): self
273 {
274 $uri = $this->uri->withPath($path);
275 if ($uri->getPath() === $this->uri->getPath()) {
276 return $this;
277 }
278
279 return new self($uri);
280 }
281
282 /**
283 * {@inheritDoc}
284 */
285 public function withQuery($query): self
286 {
287 /** @var string $query */
288 $query = $this->filterInput($query);
289 if ('' === $query) {
290 $query = null;
291 }
292
293 $uri = $this->uri->withQuery($query);
294 if ($uri->getQuery() === $this->uri->getQuery()) {
295 return $this;
296 }
297
298 return new self($uri);
299 }
300
301 /**
302 * {@inheritDoc}
303 */
304 public function withFragment($fragment): self
305 {
306 /** @var string $fragment */
307 $fragment = $this->filterInput($fragment);
308 if ('' === $fragment) {
309 $fragment = null;
310 }
311
312 $uri = $this->uri->withFragment($fragment);
313 if ($uri->getFragment() === $this->uri->getFragment()) {
314 return $this;
315 }
316
317 return new self($uri);
318 }
319
320 /**
321 * {@inheritDoc}
322 */
323 public function __toString(): string
324 {
325 return $this->uri->__toString();
326 }
327
328 /**
329 * {@inheritDoc}
330 */
331 public function jsonSerialize(): string
332 {
333 return $this->uri->__toString();
334 }
335}
336