PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 3.0.1
Defender Security – Malware Scanner, Login Security & Firewall v3.0.1
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 3.0.1, at vendor/league/uri/src/UriResolver.php

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