#php
#php
Вопрос:
Какой PHP-эквивалент для создания URL-адреса из базового URL-адреса и потенциально относительного пути? Python предоставляет urlparse.urljoin
, но, похоже, в PHP нет какой-либо стандартной реализации.
Самое близкое, что я нашел, — это люди, предлагающие использовать, parse_url
а затем перестраивать URL-адрес из частей, но реализации, делающие это, обычно приводят к ошибкам, таким как ссылки, связанные с протоколом (например, //example.com/foo
превращение в http://example.com/foo
или https://example.com/foo
, наследование протокола базового URL), и это также затрудняет обработку таких вещей, какссылки на родительский каталог. Вот примеры того, как эти вещи работают правильно в urlparse.urljoin
:
>>> from urlparse import urljoin
>>> urljoin('http://example.com/some/directory/filepart', 'foo.jpg')
'http://example.com/some/directory/foo.jpg'
>>> urljoin('http://example.com/some/directory/', 'foo.jpg')
'http://example.com/some/directory/foo.jpg'
>>> urljoin('http://example.com/some/directory/', '../foo.jpg')
'http://example.com/some/foo.jpg'
>>> urljoin('http://example.com/some/directory/', '/foo.jpg')
'http://example.com/foo.jpg'
>>> urljoin('http://example.com/some/directory/', '//images.example.com/bar.jpg')
'http://images.example.com/bar.jpg'
>>> urljoin('https://example.com/some/directory/', '//images.example.com/bar.jpg')
'https://images.example.com/bar.jpg'
>>> urljoin('ftp://example.com/some/directory/', '//images.example.com/bar.jpg')
'ftp://images.example.com/bar.jpg'
>>> urljoin('http://example.com:8080/some/directory/', '//images.example.com/bar.jpg')
'http://images.example.com/bar.jpg'
Существует ли идиоматический способ достижения того же в PHP или хорошо зарекомендовавшая себя простая библиотека или реализация, которая действительно исправляет все эти случаи?
Комментарии:
1. Я верю, что вам придется это сделать
2. @RyanVincent Я все еще не вижу там ничего, что делало бы что-то большее, чем просто
parse_url
более OO-способом. Конкатенация относительных путей намного сложнее, чем просто замена частей URL волей-неволей.
Ответ №1:
Поскольку явно существует потребность в этой функциональности, и ни один из случайных сценариев не охватывает все базы, я начал проект на Github, чтобы попытаться сделать это правильно.
В urljoin()
настоящее время реализация выглядит следующим образом:
function urljoin($base, $rel) {
$pbase = parse_url($base);
$prel = parse_url($rel);
$merged = array_merge($pbase, $prel);
if ($prel['path'][0] != '/') {
// Relative path
$dir = preg_replace('@/[^/]*$@', '', $pbase['path']);
$merged['path'] = $dir . '/' . $prel['path'];
}
// Get the path components, and remove the initial empty one
$pathParts = explode('/', $merged['path']);
array_shift($pathParts);
$path = [];
$prevPart = '';
foreach ($pathParts as $part) {
if ($part == '..' amp;amp; count($path) > 0) {
// Cancel out the parent directory (if there's a parent to cancel)
$parent = array_pop($path);
// But if it was also a parent directory, leave it in
if ($parent == '..') {
array_push($path, $parent);
array_push($path, $part);
}
} else if ($prevPart != '' || ($part != '.' amp;amp; $part != '')) {
// Don't include empty or current-directory components
if ($part == '.') {
$part = '';
}
array_push($path, $part);
}
$prevPart = $part;
}
$merged['path'] = '/' . implode('/', $path);
$ret = '';
if (isset($merged['scheme'])) {
$ret .= $merged['scheme'] . ':';
}
if (isset($merged['scheme']) || isset($merged['host'])) {
$ret .= '//';
}
if (isset($prel['host'])) {
$hostSource = $prel;
} else {
$hostSource = $pbase;
}
// username, password, and port are associated with the hostname, not merged
if (isset($hostSource['host'])) {
if (isset($hostSource['user'])) {
$ret .= $hostSource['user'];
if (isset($hostSource['pass'])) {
$ret .= ':' . $hostSource['pass'];
}
$ret .= '@';
}
$ret .= $hostSource['host'];
if (isset($hostSource['port'])) {
$ret .= ':' . $hostSource['port'];
}
}
if (isset($merged['path'])) {
$ret .= $merged['path'];
}
if (isset($prel['query'])) {
$ret .= '?' . $prel['query'];
}
if (isset($prel['fragment'])) {
$ret .= '#' . $prel['fragment'];
}
return $ret;
}
Эта функция будет корректно обрабатывать пользователей, пароли, номера портов, строки запросов, привязки и даже file:///
URL-адреса (что, по-видимому, является распространенным недостатком в существующих функциях этого типа).
Комментарии:
1. Из-за общей потребности в этом я пошел дальше и просто создал проект на github: github.com/plaidfluff/php-urljoin