PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.6.0
Defender Security – Malware Scanner, Login Security & Firewall v5.6.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / vendor / league / uri / src / UriResolver.php

UriResolver.php in Defender Security – Malware Scanner, Login Security & Firewall 5.6.0, at vendor/league/uri/src/UriResolver.php

377 lines 10.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * League.Uri (https://uri.thephpleague.com)
5 *
6 * (c) Ignace Nyamagana Butera <[email protected]>
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 declare(strict_types=1);
13
14 namespace League\Uri;
15
16 use League\Uri\Contracts\UriInterface;
17 use Psr\Http\Message\UriInterface as Psr7UriInterface;
18 use TypeError;
19 use function array_pop;
20 use function array_reduce;
21 use function count;
22 use function end;
23 use function explode;
24 use function gettype;
25 use function implode;
26 use function in_array;
27 use function sprintf;
28 use function str_repeat;
29 use function strpos;
30 use function substr;
31
32 final class UriResolver
33 {
34 /**
35 * @var array<string,int>
36 */
37 const DOT_SEGMENTS = ['.' => 1, '..' => 1];
38
39 /**
40 * @codeCoverageIgnore
41 */
42 private function __construct()
43 {
44 }
45
46 /**
47 * Resolve an URI against a base URI using RFC3986 rules.
48 *
49 * If the first argument is a UriInterface the method returns a UriInterface object
50 * If the first argument is a Psr7UriInterface the method returns a Psr7UriInterface object
51 *
52 * @param Psr7UriInterface|UriInterface $uri
53 * @param Psr7UriInterface|UriInterface $base_uri
54 *
55 * @return Psr7UriInterface|UriInterface
56 */
57 public static function resolve($uri, $base_uri)
58 {
59 self::filterUri($uri);
60 self::filterUri($base_uri);
61 $null = $uri instanceof Psr7UriInterface ? '' : null;
62
63 if ($null !== $uri->getScheme()) {
64 return $uri
65 ->withPath(self::removeDotSegments($uri->getPath()));
66 }
67
68 if ($null !== $uri->getAuthority()) {
69 return $uri
70 ->withScheme($base_uri->getScheme())
71 ->withPath(self::removeDotSegments($uri->getPath()));
72 }
73
74 $user = $null;
75 $pass = null;
76 $userInfo = $base_uri->getUserInfo();
77 if (null !== $userInfo) {
78 [$user, $pass] = explode(':', $userInfo, 2) + [1 => null];
79 }
80
81 [$uri_path, $uri_query] = self::resolvePathAndQuery($uri, $base_uri);
82
83 return $uri
84 ->withPath(self::removeDotSegments($uri_path))
85 ->withQuery($uri_query)
86 ->withHost($base_uri->getHost())
87 ->withPort($base_uri->getPort())
88 ->withUserInfo((string) $user, $pass)
89 ->withScheme($base_uri->getScheme())
90 ;
91 }
92
93 /**
94 * Filter the URI object.
95 *
96 * @param mixed $uri an URI object
97 *
98 * @throws TypeError if the URI object does not implements the supported interfaces.
99 */
100 private static function filterUri($uri): void
101 {
102 if (!$uri instanceof UriInterface && !$uri instanceof Psr7UriInterface) {
103 throw new TypeError(sprintf('The uri must be a valid URI object received `%s`', gettype($uri)));
104 }
105 }
106
107 /**
108 * Remove dot segments from the URI path.
109 */
110 private static function removeDotSegments(string $path): string
111 {
112 if (false === strpos($path, '.')) {
113 return $path;
114 }
115
116 $old_segments = explode('/', $path);
117 $new_path = implode('/', array_reduce($old_segments, [UriResolver::class, 'reducer'], []));
118 if (isset(self::DOT_SEGMENTS[end($old_segments)])) {
119 $new_path .= '/';
120 }
121
122 // @codeCoverageIgnoreStart
123 // added because some PSR-7 implementations do not respect RFC3986
124 if (0 === strpos($path, '/') && 0 !== strpos($new_path, '/')) {
125 return '/'.$new_path;
126 }
127 // @codeCoverageIgnoreEnd
128
129 return $new_path;
130 }
131
132 /**
133 * Remove dot segments.
134 *
135 * @return array<int, string>
136 */
137 private static function reducer(array $carry, string $segment): array
138 {
139 if ('..' === $segment) {
140 array_pop($carry);
141
142 return $carry;
143 }
144
145 if (!isset(self::DOT_SEGMENTS[$segment])) {
146 $carry[] = $segment;
147 }
148
149 return $carry;
150 }
151
152 /**
153 * Resolve an URI path and query component.
154 *
155 * @param Psr7UriInterface|UriInterface $uri
156 * @param Psr7UriInterface|UriInterface $base_uri
157 *
158 * @return array{0:string, 1:string|null}
159 */
160 private static function resolvePathAndQuery($uri, $base_uri): array
161 {
162 $target_path = $uri->getPath();
163 $target_query = $uri->getQuery();
164 $null = $uri instanceof Psr7UriInterface ? '' : null;
165 $baseNull = $base_uri instanceof Psr7UriInterface ? '' : null;
166
167 if (0 === strpos($target_path, '/')) {
168 return [$target_path, $target_query];
169 }
170
171 if ('' === $target_path) {
172 if ($null === $target_query) {
173 $target_query = $base_uri->getQuery();
174 }
175
176 $target_path = $base_uri->getPath();
177 //@codeCoverageIgnoreStart
178 //because some PSR-7 Uri implementations allow this RFC3986 forbidden construction
179 if ($baseNull !== $base_uri->getAuthority() && 0 !== strpos($target_path, '/')) {
180 $target_path = '/'.$target_path;
181 }
182 //@codeCoverageIgnoreEnd
183
184 return [$target_path, $target_query];
185 }
186
187 $base_path = $base_uri->getPath();
188 if ($baseNull !== $base_uri->getAuthority() && '' === $base_path) {
189 $target_path = '/'.$target_path;
190 }
191
192 if ('' !== $base_path) {
193 $segments = explode('/', $base_path);
194 array_pop($segments);
195 if ([] !== $segments) {
196 $target_path = implode('/', $segments).'/'.$target_path;
197 }
198 }
199
200 return [$target_path, $target_query];
201 }
202
203 /**
204 * Relativize an URI according to a base URI.
205 *
206 * This method MUST retain the state of the submitted URI instance, and return
207 * an URI instance of the same type that contains the applied modifications.
208 *
209 * This method MUST be transparent when dealing with error and exceptions.
210 * It MUST not alter of silence them apart from validating its own parameters.
211 *
212 * @param Psr7UriInterface|UriInterface $uri
213 * @param Psr7UriInterface|UriInterface $base_uri
214 *
215 * @return Psr7UriInterface|UriInterface
216 */
217 public static function relativize($uri, $base_uri)
218 {
219 self::filterUri($uri);
220 self::filterUri($base_uri);
221 $uri = self::formatHost($uri);
222 $base_uri = self::formatHost($base_uri);
223 if (!self::isRelativizable($uri, $base_uri)) {
224 return $uri;
225 }
226
227 $null = $uri instanceof Psr7UriInterface ? '' : null;
228 $uri = $uri->withScheme($null)->withPort(null)->withUserInfo($null)->withHost($null);
229 $target_path = $uri->getPath();
230 if ($target_path !== $base_uri->getPath()) {
231 return $uri->withPath(self::relativizePath($target_path, $base_uri->getPath()));
232 }
233
234 if (self::componentEquals('getQuery', $uri, $base_uri)) {
235 return $uri->withPath('')->withQuery($null);
236 }
237
238 if ($null === $uri->getQuery()) {
239 return $uri->withPath(self::formatPathWithEmptyBaseQuery($target_path));
240 }
241
242 return $uri->withPath('');
243 }
244
245 /**
246 * Tells whether the component value from both URI object equals.
247 *
248 * @param Psr7UriInterface|UriInterface $uri
249 * @param Psr7UriInterface|UriInterface $base_uri
250 */
251 private static function componentEquals(string $method, $uri, $base_uri): bool
252 {
253 return self::getComponent($method, $uri) === self::getComponent($method, $base_uri);
254 }
255
256 /**
257 * Returns the component value from the submitted URI object.
258 *
259 * @param Psr7UriInterface|UriInterface $uri
260 */
261 private static function getComponent(string $method, $uri): ?string
262 {
263 $component = $uri->$method();
264 if ($uri instanceof Psr7UriInterface && '' === $component) {
265 return null;
266 }
267
268 return $component;
269 }
270
271 /**
272 * Filter the URI object.
273 *
274 * @param Psr7UriInterface|UriInterface $uri
275 *
276 * @throws TypeError if the URI object does not implements the supported interfaces.
277 *
278 * @return Psr7UriInterface|UriInterface
279 */
280 private static function formatHost($uri)
281 {
282 if (!$uri instanceof Psr7UriInterface) {
283 return $uri;
284 }
285
286 $host = $uri->getHost();
287 if ('' === $host) {
288 return $uri;
289 }
290
291 return $uri->withHost((string) Uri::createFromComponents(['host' => $host])->getHost());
292 }
293
294 /**
295 * Tell whether the submitted URI object can be relativize.
296 *
297 * @param Psr7UriInterface|UriInterface $uri
298 * @param Psr7UriInterface|UriInterface $base_uri
299 */
300 private static function isRelativizable($uri, $base_uri): bool
301 {
302 return !UriInfo::isRelativePath($uri)
303 && self::componentEquals('getScheme', $uri, $base_uri)
304 && self::componentEquals('getAuthority', $uri, $base_uri);
305 }
306
307 /**
308 * Relative the URI for a authority-less target URI.
309 */
310 private static function relativizePath(string $path, string $basepath): string
311 {
312 $base_segments = self::getSegments($basepath);
313 $target_segments = self::getSegments($path);
314 $target_basename = array_pop($target_segments);
315 array_pop($base_segments);
316 foreach ($base_segments as $offset => $segment) {
317 if (!isset($target_segments[$offset]) || $segment !== $target_segments[$offset]) {
318 break;
319 }
320 unset($base_segments[$offset], $target_segments[$offset]);
321 }
322 $target_segments[] = $target_basename;
323
324 return self::formatPath(
325 str_repeat('../', count($base_segments)).implode('/', $target_segments),
326 $basepath
327 );
328 }
329
330 /**
331 * returns the path segments.
332 *
333 * @return string[]
334 */
335 private static function getSegments(string $path): array
336 {
337 if ('' !== $path && '/' === $path[0]) {
338 $path = substr($path, 1);
339 }
340
341 return explode('/', $path);
342 }
343
344 /**
345 * Formatting the path to keep a valid URI.
346 */
347 private static function formatPath(string $path, string $basepath): string
348 {
349 if ('' === $path) {
350 return in_array($basepath, ['', '/'], true) ? $basepath : './';
351 }
352
353 if (false === ($colon_pos = strpos($path, ':'))) {
354 return $path;
355 }
356
357 $slash_pos = strpos($path, '/');
358 if (false === $slash_pos || $colon_pos < $slash_pos) {
359 return "./$path";
360 }
361
362 return $path;
363 }
364
365 /**
366 * Formatting the path to keep a resolvable URI.
367 */
368 private static function formatPathWithEmptyBaseQuery(string $path): string
369 {
370 $target_segments = self::getSegments($path);
371 /** @var string $basename */
372 $basename = end($target_segments);
373
374 return '' === $basename ? './' : $basename;
375 }
376 }
377