final class UriResolver in Lockr 7.3
Resolves a URI reference in the context of a base URI and the opposite way.
@author Tobias Schultze
@link https://tools.ietf.org/html/rfc3986#section-5
Hierarchy
- class \GuzzleHttp\Psr7\UriResolver
Expanded class hierarchy of UriResolver
File
- vendor/
guzzlehttp/ psr7/ src/ UriResolver.php, line 13
Namespace
GuzzleHttp\Psr7View source
final class UriResolver {
/**
* Removes dot segments from a path and returns the new path.
*
* @param string $path
*
* @return string
* @link http://tools.ietf.org/html/rfc3986#section-5.2.4
*/
public static function removeDotSegments($path) {
if ($path === '' || $path === '/') {
return $path;
}
$results = [];
$segments = explode('/', $path);
foreach ($segments as $segment) {
if ($segment === '..') {
array_pop($results);
}
elseif ($segment !== '.') {
$results[] = $segment;
}
}
$newPath = implode('/', $results);
if ($path[0] === '/' && (!isset($newPath[0]) || $newPath[0] !== '/')) {
// Re-add the leading slash if necessary for cases like "/.."
$newPath = '/' . $newPath;
}
elseif ($newPath !== '' && ($segment === '.' || $segment === '..')) {
// Add the trailing slash if necessary
// If newPath is not empty, then $segment must be set and is the last segment from the foreach
$newPath .= '/';
}
return $newPath;
}
/**
* Converts the relative URI into a new URI that is resolved against the base URI.
*
* @param UriInterface $base Base URI
* @param UriInterface $rel Relative URI
*
* @return UriInterface
* @link http://tools.ietf.org/html/rfc3986#section-5.2
*/
public static function resolve(UriInterface $base, UriInterface $rel) {
if ((string) $rel === '') {
// we can simply return the same base URI instance for this same-document reference
return $base;
}
if ($rel
->getScheme() != '') {
return $rel
->withPath(self::removeDotSegments($rel
->getPath()));
}
if ($rel
->getAuthority() != '') {
$targetAuthority = $rel
->getAuthority();
$targetPath = self::removeDotSegments($rel
->getPath());
$targetQuery = $rel
->getQuery();
}
else {
$targetAuthority = $base
->getAuthority();
if ($rel
->getPath() === '') {
$targetPath = $base
->getPath();
$targetQuery = $rel
->getQuery() != '' ? $rel
->getQuery() : $base
->getQuery();
}
else {
if ($rel
->getPath()[0] === '/') {
$targetPath = $rel
->getPath();
}
else {
if ($targetAuthority != '' && $base
->getPath() === '') {
$targetPath = '/' . $rel
->getPath();
}
else {
$lastSlashPos = strrpos($base
->getPath(), '/');
if ($lastSlashPos === false) {
$targetPath = $rel
->getPath();
}
else {
$targetPath = substr($base
->getPath(), 0, $lastSlashPos + 1) . $rel
->getPath();
}
}
}
$targetPath = self::removeDotSegments($targetPath);
$targetQuery = $rel
->getQuery();
}
}
return new Uri(Uri::composeComponents($base
->getScheme(), $targetAuthority, $targetPath, $targetQuery, $rel
->getFragment()));
}
/**
* Returns the target URI as a relative reference from the base URI.
*
* This method is the counterpart to resolve():
*
* (string) $target === (string) UriResolver::resolve($base, UriResolver::relativize($base, $target))
*
* One use-case is to use the current request URI as base URI and then generate relative links in your documents
* to reduce the document size or offer self-contained downloadable document archives.
*
* $base = new Uri('http://example.com/a/b/');
* echo UriResolver::relativize($base, new Uri('http://example.com/a/b/c')); // prints 'c'.
* echo UriResolver::relativize($base, new Uri('http://example.com/a/x/y')); // prints '../x/y'.
* echo UriResolver::relativize($base, new Uri('http://example.com/a/b/?q')); // prints '?q'.
* echo UriResolver::relativize($base, new Uri('http://example.org/a/b/')); // prints '//example.org/a/b/'.
*
* This method also accepts a target that is already relative and will try to relativize it further. Only a
* relative-path reference will be returned as-is.
*
* echo UriResolver::relativize($base, new Uri('/a/b/c')); // prints 'c' as well
*
* @param UriInterface $base Base URI
* @param UriInterface $target Target URI
*
* @return UriInterface The relative URI reference
*/
public static function relativize(UriInterface $base, UriInterface $target) {
if ($target
->getScheme() !== '' && ($base
->getScheme() !== $target
->getScheme() || $target
->getAuthority() === '' && $base
->getAuthority() !== '')) {
return $target;
}
if (Uri::isRelativePathReference($target)) {
// As the target is already highly relative we return it as-is. It would be possible to resolve
// the target with `$target = self::resolve($base, $target);` and then try make it more relative
// by removing a duplicate query. But let's not do that automatically.
return $target;
}
if ($target
->getAuthority() !== '' && $base
->getAuthority() !== $target
->getAuthority()) {
return $target
->withScheme('');
}
// We must remove the path before removing the authority because if the path starts with two slashes, the URI
// would turn invalid. And we also cannot set a relative path before removing the authority, as that is also
// invalid.
$emptyPathUri = $target
->withScheme('')
->withPath('')
->withUserInfo('')
->withPort(null)
->withHost('');
if ($base
->getPath() !== $target
->getPath()) {
return $emptyPathUri
->withPath(self::getRelativePath($base, $target));
}
if ($base
->getQuery() === $target
->getQuery()) {
// Only the target fragment is left. And it must be returned even if base and target fragment are the same.
return $emptyPathUri
->withQuery('');
}
// If the base URI has a query but the target has none, we cannot return an empty path reference as it would
// inherit the base query component when resolving.
if ($target
->getQuery() === '') {
$segments = explode('/', $target
->getPath());
$lastSegment = end($segments);
return $emptyPathUri
->withPath($lastSegment === '' ? './' : $lastSegment);
}
return $emptyPathUri;
}
private static function getRelativePath(UriInterface $base, UriInterface $target) {
$sourceSegments = explode('/', $base
->getPath());
$targetSegments = explode('/', $target
->getPath());
array_pop($sourceSegments);
$targetLastSegment = array_pop($targetSegments);
foreach ($sourceSegments as $i => $segment) {
if (isset($targetSegments[$i]) && $segment === $targetSegments[$i]) {
unset($sourceSegments[$i], $targetSegments[$i]);
}
else {
break;
}
}
$targetSegments[] = $targetLastSegment;
$relativePath = str_repeat('../', count($sourceSegments)) . implode('/', $targetSegments);
// A reference to am empty last segment or an empty first sub-segment must be prefixed with "./".
// This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
// as the first segment of a relative-path reference, as it would be mistaken for a scheme name.
if ('' === $relativePath || false !== strpos(explode('/', $relativePath, 2)[0], ':')) {
$relativePath = "./{$relativePath}";
}
elseif ('/' === $relativePath[0]) {
if ($base
->getAuthority() != '' && $base
->getPath() === '') {
// In this case an extra slash is added by resolve() automatically. So we must not add one here.
$relativePath = ".{$relativePath}";
}
else {
$relativePath = "./{$relativePath}";
}
}
return $relativePath;
}
private function __construct() {
// cannot be instantiated
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
UriResolver:: |
private static | function | ||
UriResolver:: |
public static | function | Returns the target URI as a relative reference from the base URI. | |
UriResolver:: |
public static | function | Removes dot segments from a path and returns the new path. | |
UriResolver:: |
public static | function | Converts the relative URI into a new URI that is resolved against the base URI. | |
UriResolver:: |
private | function |