| 1 |
<?php |
| 2 |
|
| 3 |
/* |
| 4 |
* This file is part of the Symfony package. |
| 5 |
* |
| 6 |
* (c) Fabien Potencier <[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 |
namespace Symfony\Component\DomCrawler; |
| 13 |
|
| 14 |
/** |
| 15 |
* The UriResolver class takes an URI (relative, absolute, fragment, etc.) |
| 16 |
* and turns it into an absolute URI against another given base URI. |
| 17 |
* |
| 18 |
* @author Fabien Potencier <[email protected]> |
| 19 |
* @author Grégoire Pineau <[email protected]> |
| 20 |
*/ |
| 21 |
class UriResolver |
| 22 |
{ |
| 23 |
/** |
| 24 |
* Resolves a URI according to a base URI. |
| 25 |
* |
| 26 |
* For example if $uri=/foo/bar and $baseUri=https://symfony.com it will |
| 27 |
* return https://symfony.com/foo/bar |
| 28 |
* |
| 29 |
* If the $uri is not absolute you must pass an absolute $baseUri |
| 30 |
*/ |
| 31 |
public static function resolve(string $uri, ?string $baseUri): string |
| 32 |
{ |
| 33 |
$uri = trim($uri); |
| 34 |
|
| 35 |
// absolute URL? |
| 36 |
if (null !== parse_url(\strlen($uri) !== strcspn($uri, '?#') ? $uri : $uri.'#', \PHP_URL_SCHEME)) { |
| 37 |
return $uri; |
| 38 |
} |
| 39 |
|
| 40 |
if (null === $baseUri) { |
| 41 |
throw new \InvalidArgumentException('The URI is relative, so you must define its base URI passing an absolute URL.'); |
| 42 |
} |
| 43 |
|
| 44 |
// empty URI |
| 45 |
if (!$uri) { |
| 46 |
return $baseUri; |
| 47 |
} |
| 48 |
|
| 49 |
// an anchor |
| 50 |
if ('#' === $uri[0]) { |
| 51 |
return self::cleanupAnchor($baseUri).$uri; |
| 52 |
} |
| 53 |
|
| 54 |
$baseUriCleaned = self::cleanupUri($baseUri); |
| 55 |
|
| 56 |
if ('?' === $uri[0]) { |
| 57 |
return $baseUriCleaned.$uri; |
| 58 |
} |
| 59 |
|
| 60 |
// absolute URL with relative schema |
| 61 |
if (0 === strpos($uri, '//')) { |
| 62 |
return preg_replace('#^([^/]*)//.*$#', '$1', $baseUriCleaned).$uri; |
| 63 |
} |
| 64 |
|
| 65 |
$baseUriCleaned = preg_replace('#^(.*?//[^/]*)(?:\/.*)?$#', '$1', $baseUriCleaned); |
| 66 |
|
| 67 |
// absolute path |
| 68 |
if ('/' === $uri[0]) { |
| 69 |
return $baseUriCleaned.$uri; |
| 70 |
} |
| 71 |
|
| 72 |
// relative path |
| 73 |
$path = parse_url(substr($baseUri, \strlen($baseUriCleaned)), \PHP_URL_PATH) ?? ''; |
| 74 |
$path = self::canonicalizePath(substr($path, 0, strrpos($path, '/')).'/'.$uri); |
| 75 |
|
| 76 |
return $baseUriCleaned.('' === $path || '/' !== $path[0] ? '/' : '').$path; |
| 77 |
} |
| 78 |
|
| 79 |
/** |
| 80 |
* Returns the canonicalized URI path (see RFC 3986, section 5.2.4). |
| 81 |
*/ |
| 82 |
private static function canonicalizePath(string $path): string |
| 83 |
{ |
| 84 |
if ('' === $path || '/' === $path) { |
| 85 |
return $path; |
| 86 |
} |
| 87 |
|
| 88 |
if ('.' === substr($path, -1)) { |
| 89 |
$path .= '/'; |
| 90 |
} |
| 91 |
|
| 92 |
$output = []; |
| 93 |
|
| 94 |
foreach (explode('/', $path) as $segment) { |
| 95 |
if ('..' === $segment) { |
| 96 |
array_pop($output); |
| 97 |
} elseif ('.' !== $segment) { |
| 98 |
$output[] = $segment; |
| 99 |
} |
| 100 |
} |
| 101 |
|
| 102 |
return implode('/', $output); |
| 103 |
} |
| 104 |
|
| 105 |
/** |
| 106 |
* Removes the query string and the anchor from the given uri. |
| 107 |
*/ |
| 108 |
private static function cleanupUri(string $uri): string |
| 109 |
{ |
| 110 |
return self::cleanupQuery(self::cleanupAnchor($uri)); |
| 111 |
} |
| 112 |
|
| 113 |
/** |
| 114 |
* Removes the query string from the uri. |
| 115 |
*/ |
| 116 |
private static function cleanupQuery(string $uri): string |
| 117 |
{ |
| 118 |
if (false !== $pos = strpos($uri, '?')) { |
| 119 |
return substr($uri, 0, $pos); |
| 120 |
} |
| 121 |
|
| 122 |
return $uri; |
| 123 |
} |
| 124 |
|
| 125 |
/** |
| 126 |
* Removes the anchor from the uri. |
| 127 |
*/ |
| 128 |
private static function cleanupAnchor(string $uri): string |
| 129 |
{ |
| 130 |
if (false !== $pos = strpos($uri, '#')) { |
| 131 |
return substr($uri, 0, $pos); |
| 132 |
} |
| 133 |
|
| 134 |
return $uri; |
| 135 |
} |
| 136 |
} |
| 137 |
|