Как получить окончательный URL-адрес после перенаправления HTTP в чистом PHP?
То, что я хотел бы сделать, это узнать, что является последним/окончательным URL после следующих перенаправления.
Я бы предпочел не использовать cURL. Я хотел бы придерживаться чистого PHP (обтекатели потоков).
Сейчас у меня есть URL (скажем http://domain.test), и я использую get_headers() для получения определенных заголовков с этой страницы. get_headers также возвращают несколько заголовков Location:
(см. Изменить ниже). Есть ли способ использовать эти заголовки для создания конечного URL-адреса? или есть функция PHP, которая автоматически сделает это?
Изменить: get_headers() следует перенаправлениям и возвращает все заголовки для каждого ответа/перенаправления, поэтому у меня есть все заголовки Location:
.
Ответы
Ответ 1
/**
* get_redirect_url()
* Gets the address that the provided URL redirects to,
* or FALSE if there no redirect.
*
* @param string $url
* @return string
*/
function get_redirect_url($url){
$redirect_url = null;
$url_parts = @parse_url($url);
if (!$url_parts) return false;
if (!isset($url_parts['host'])) return false; //can't process relative URLs
if (!isset($url_parts['path'])) $url_parts['path'] = '/';
$sock = fsockopen($url_parts['host'], (isset($url_parts['port']) ? (int)$url_parts['port'] : 80), $errno, $errstr, 30);
if (!$sock) return false;
$request = "HEAD " . $url_parts['path'] . (isset($url_parts['query']) ? '?'.$url_parts['query'] : '') . " HTTP/1.1\r\n";
$request .= 'Host: ' . $url_parts['host'] . "\r\n";
$request .= "Connection: Close\r\n\r\n";
fwrite($sock, $request);
$response = '';
while(!feof($sock)) $response .= fread($sock, 8192);
fclose($sock);
if (preg_match('/^Location: (.+?)$/m', $response, $matches)){
if ( substr($matches[1], 0, 1) == "/" )
return $url_parts['scheme'] . "://" . $url_parts['host'] . trim($matches[1]);
else
return trim($matches[1]);
} else {
return false;
}
}
/**
* get_all_redirects()
* Follows and collects all redirects, in order, for the given URL.
*
* @param string $url
* @return array
*/
function get_all_redirects($url){
$redirects = array();
while ($newurl = get_redirect_url($url)){
if (in_array($newurl, $redirects)){
break;
}
$redirects[] = $newurl;
$url = $newurl;
}
return $redirects;
}
/**
* get_final_url()
* Gets the address that the URL ultimately leads to.
* Returns $url itself if it isn't a redirect.
*
* @param string $url
* @return string
*/
function get_final_url($url){
$redirects = get_all_redirects($url);
if (count($redirects)>0){
return array_pop($redirects);
} else {
return $url;
}
}
И, как всегда, отдать должное:
http://w-shadow.com/blog/2008/07/05/how-to-get-redirect-url-in-php/
Ответ 2
function getRedirectUrl ($url) {
stream_context_set_default(array(
'http' => array(
'method' => 'HEAD'
)
));
$headers = get_headers($url, 1);
if ($headers !== false && isset($headers['Location'])) {
return $headers['Location'];
}
return false;
}
Дополнительно...
Как уже упоминалось в комментарии, конечный элемент в $headers['Location']
будет вашим окончательным URL после всех перенаправлений. Важно отметить, однако, что он не всегда будет массивом. Иногда это просто переменная run-of-the-mill, non-array. В этом случае попытка доступа к последнему элементу массива скорее всего вернет один символ. Не идеально.
Если вас интересует только конечный URL-адрес, после всех переадресаций я предлагаю изменить
return $headers['Location'];
к
return is_array($headers['Location']) ? array_pop($headers['Location']) : $headers['Location'];
... это просто если коротко нажмите для
if(is_array($headers['Location'])){
return array_pop($headers['Location']);
}else{
return $headers['Location'];
}
Это исправление позаботится обо всех случаях (массив, не-массив) и устранит необходимость отсеивания конечного URL после вызова функции.
В случае отсутствия переадресаций функция вернет false
. Аналогично, функция также вернет false
для недопустимых URL-адресов (по какой-либо причине недействительна). Поэтому перед тем, как запускать эту функцию, важно проверить URL-адрес для валидности или включить проверку перенаправления в вашу проверку.
Ответ 3
xaav ответ очень хорош; за исключением следующих двух проблем:
- Он не поддерживает протокол HTTPS = > Решение было предложено в качестве комментария на исходном сайте: http://w-shadow.com/blog/2008/07/05/how-to-get-redirect-url-in-php/
-
Некоторые сайты не будут работать, поскольку они не будут распознавать базовый пользовательский агент (клиентский браузер)
= > Это просто фиксируется добавлением поля заголовка User-agent: я добавил пользовательский агент Android (здесь вы можете найти http://www.useragentstring.com/pages/useragentstring.php другого пользователя примеры агентов в соответствии с вашими потребностями):
$request. = "User-Agent: Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, как и Gecko) Версия /4.0 Mobile Safari/534.30\r\n";
Здесь измененный ответ:
/**
* get_redirect_url()
* Gets the address that the provided URL redirects to,
* or FALSE if there no redirect.
*
* @param string $url
* @return string
*/
function get_redirect_url($url){
$redirect_url = null;
$url_parts = @parse_url($url);
if (!$url_parts) return false;
if (!isset($url_parts['host'])) return false; //can't process relative URLs
if (!isset($url_parts['path'])) $url_parts['path'] = '/';
$sock = fsockopen($url_parts['host'], (isset($url_parts['port']) ? (int)$url_parts['port'] : 80), $errno, $errstr, 30);
if (!$sock) return false;
$request = "HEAD " . $url_parts['path'] . (isset($url_parts['query']) ? '?'.$url_parts['query'] : '') . " HTTP/1.1\r\n";
$request .= 'Host: ' . $url_parts['host'] . "\r\n";
$request .= "User-Agent: Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30\r\n";
$request .= "Connection: Close\r\n\r\n";
fwrite($sock, $request);
$response = '';
while(!feof($sock)) $response .= fread($sock, 8192);
fclose($sock);
if (preg_match('/^Location: (.+?)$/m', $response, $matches)){
if ( substr($matches[1], 0, 1) == "/" )
return $url_parts['scheme'] . "://" . $url_parts['host'] . trim($matches[1]);
else
return trim($matches[1]);
} else {
return false;
}
}
/**
* get_all_redirects()
* Follows and collects all redirects, in order, for the given URL.
*
* @param string $url
* @return array
*/
function get_all_redirects($url){
$redirects = array();
while ($newurl = get_redirect_url($url)){
if (in_array($newurl, $redirects)){
break;
}
$redirects[] = $newurl;
$url = $newurl;
}
return $redirects;
}
/**
* get_final_url()
* Gets the address that the URL ultimately leads to.
* Returns $url itself if it isn't a redirect.
*
* @param string $url
* @return string
*/
function get_final_url($url){
$redirects = get_all_redirects($url);
if (count($redirects)>0){
return array_pop($redirects);
} else {
return $url;
}
Ответ 4
В то время как OP хотел избежать cURL
, лучше использовать его, когда он доступен. Здесь решение, имеющее следующие преимущества
- использует завиток для всего тяжелого подъема, поэтому работает с https
- справляется с серверами, которые возвращают более низкое имя заголовка
location
(оба ответа xaav и webjay не обрабатывают это)
- позволяет вам контролировать, насколько глубоко вы хотите, прежде чем отказаться от него.
Здесь функция:
function findUltimateDestination($url, $maxRequests = 10)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_MAXREDIRS, $maxRequests);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
//customize user agent if you desire...
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Link Checker)');
curl_setopt($ch, CURLOPT_URL, $url);
curl_exec($ch);
$url=curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
curl_close ($ch);
return $url;
}
Здесь представлена более подробная версия, которая позволяет вам проверять цепочку перенаправления, а не следить за ней.
function findUltimateDestination($url, $maxRequests = 10)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
//customize user agent if you desire...
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Link Checker)');
while ($maxRequests--) {
//fetch
curl_setopt($ch, CURLOPT_URL, $url);
$response = curl_exec($ch);
//try to determine redirection url
$location = '';
if (in_array(curl_getinfo($ch, CURLINFO_HTTP_CODE), [301, 302, 303, 307, 308])) {
if (preg_match('/Location:(.*)/i', $response, $match)) {
$location = trim($match[1]);
}
}
if (empty($location)) {
//we've reached the end of the chain...
return $url;
}
//build next url
if ($location[0] == '/') {
$u = parse_url($url);
$url = $u['scheme'] . '://' . $u['host'];
if (isset($u['port'])) {
$url .= ':' . $u['port'];
}
$url .= $location;
} else {
$url = $location;
}
}
return null;
}
В качестве примера цепочки перенаправления, которую обрабатывает эта функция, а другие нет, попробуйте следующее:
echo findUltimateDestination('http://dx.doi.org/10.1016/j.infsof.2016.05.005')
Во время записи это включает в себя 4 запроса со смешанными заголовками location
и location
.