157 lines
4.1 KiB
PHP
157 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Doctrine\RST;
|
|
|
|
use function array_pop;
|
|
use function basename;
|
|
use function count;
|
|
use function explode;
|
|
use function implode;
|
|
use function ltrim;
|
|
use function preg_match;
|
|
use function rtrim;
|
|
use function strpos;
|
|
use function substr;
|
|
|
|
final class UrlGenerator
|
|
{
|
|
/** @var Configuration */
|
|
private $configuration;
|
|
|
|
public function __construct(Configuration $configuration)
|
|
{
|
|
$this->configuration = $configuration;
|
|
}
|
|
|
|
public function generateUrl(string $path, string $currentFileName, string $dirName): string
|
|
{
|
|
$canonicalPath = (string) $this->canonicalUrl($dirName, $path);
|
|
|
|
if ($this->configuration->isBaseUrlEnabled($canonicalPath)) {
|
|
$baseUrl = $this->configuration->getBaseUrl();
|
|
|
|
return rtrim($baseUrl, '/') . '/' . ltrim($canonicalPath, '/');
|
|
}
|
|
|
|
return (string) $this->relativeUrl($path, $currentFileName);
|
|
}
|
|
|
|
public function absoluteUrl(string $dirName, string $url): string
|
|
{
|
|
// if $url is already an absolute path, just return it
|
|
if ($url[0] === '/') {
|
|
return $url;
|
|
}
|
|
|
|
// $url is a relative path so join it together with the
|
|
// current $dirName to produce an absolute url
|
|
return rtrim($dirName, '/') . '/' . $url;
|
|
}
|
|
|
|
/**
|
|
* Resolves a relative URL using directories, for instance, if the
|
|
* current directory is "path/to/something", and you want to get the
|
|
* relative URL to "path/to/something/else.html", the result will
|
|
* be else.html. Else, "../" will be added to go to the upper directory
|
|
*/
|
|
public function relativeUrl(?string $url, string $currentFileName): ?string
|
|
{
|
|
if ($url === null) {
|
|
return null;
|
|
}
|
|
|
|
// If string contains ://, it is considered as absolute
|
|
if (preg_match('/:\\/\\//mUsi', $url) > 0) {
|
|
return $url;
|
|
}
|
|
|
|
// If string begins with "/", the "/" is removed to resolve the
|
|
// relative path
|
|
if ($url !== '' && $url[0] === '/') {
|
|
$url = substr($url, 1);
|
|
|
|
if ($this->samePrefix($url, $currentFileName)) {
|
|
// If the prefix is the same, simply returns the file name
|
|
$relative = basename($url);
|
|
} else {
|
|
// Else, returns enough ../ to get upper
|
|
$relative = '';
|
|
|
|
$depth = count(explode('/', $currentFileName)) - 1;
|
|
|
|
for ($k = 0; $k < $depth; $k++) {
|
|
$relative .= '../';
|
|
}
|
|
|
|
$relative .= $url;
|
|
}
|
|
} else {
|
|
$relative = $url;
|
|
}
|
|
|
|
return $relative;
|
|
}
|
|
|
|
public function canonicalUrl(string $dirName, string $url): ?string
|
|
{
|
|
if ($url !== '') {
|
|
if ($url[0] === '/') {
|
|
// If the URL begins with a "/", the following is the
|
|
// canonical URL
|
|
return substr($url, 1);
|
|
}
|
|
|
|
// Else, the canonical name is under the current dir
|
|
if ($dirName !== '') {
|
|
$path = $url;
|
|
|
|
// the url is already a canonical url
|
|
if (strpos($url, $dirName . '/') === 0) {
|
|
return $url;
|
|
}
|
|
|
|
return $this->canonicalize($dirName . '/' . $path);
|
|
}
|
|
|
|
return $this->canonicalize($url);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private function canonicalize(string $url): string
|
|
{
|
|
$parts = explode('/', $url);
|
|
$stack = [];
|
|
|
|
foreach ($parts as $part) {
|
|
if ($part === '..') {
|
|
array_pop($stack);
|
|
} else {
|
|
$stack[] = $part;
|
|
}
|
|
}
|
|
|
|
return implode('/', $stack);
|
|
}
|
|
|
|
private function samePrefix(string $url, string $currentFileName): bool
|
|
{
|
|
$partsA = explode('/', $url);
|
|
$partsB = explode('/', $currentFileName);
|
|
|
|
$n = count($partsA);
|
|
|
|
if ($n !== count($partsB)) {
|
|
return false;
|
|
}
|
|
|
|
unset($partsA[$n - 1]);
|
|
unset($partsB[$n - 1]);
|
|
|
|
return $partsA === $partsB;
|
|
}
|
|
}
|