私がやりたいのはリダイレクトをたどった後の最後/最後のURLを見つけることです。
cURLは使いたくないです。純粋なPHP(ストリームラッパー))に固執したいと思います。
現在、私はURLを持っており(たとえば http://domain.test )、get_headers()を使用してそのページから特定のヘッダーを取得しています。 get_headersは、複数のLocation:
ヘッダーも返します(以下のEditを参照)。これらのヘッダーを使用して最終的なURLを作成する方法はありますか?または、これを自動的に行うPHP関数はありますか?
編集:get_headers()はリダイレクトに従い、各応答/リダイレクトのすべてのヘッダーを返すため、すべてのLocation:
ヘッダーがあります。
/**
* get_redirect_url()
* Gets the address that the provided URL redirects to,
* or FALSE if there's 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/
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']
の-finalアイテムは、すべてのリダイレクト後の最終URLになります。ただし、配列ではないことに注意することが重要です常に。場合によっては、それは単なるありふれた非配列変数です。この場合、最後の配列要素にアクセスしようとすると、ほとんどの場合1文字が返されます。理想的ではありません。
最終的なURLのみに関心がある場合は、すべてのリダイレクトの後、変更することをお勧めします
return $headers['Location'];
に
return is_array($headers['Location']) ? array_pop($headers['Location']) : $headers['Location'];
...これは 速記の場合 for
if(is_array($headers['Location'])){
return array_pop($headers['Location']);
}else{
return $headers['Location'];
}
この修正により、いずれかの場合(配列、非配列)が処理され、関数の呼び出し後に最終的なURLを削除する必要がなくなります。
リダイレクトがない場合、関数はfalse
を返します。同様に、この関数は無効なURL(何らかの理由で無効)に対してもfalse
を返します。そのため、 RLの有効性を確認するbeforeこの関数を実行するか、リダイレクトチェックを検証のどこかに組み込むことが重要です。
OPはcURL
を避けたかったのですが、利用可能な場合はそれを使用するのが最善です。これが次の利点を持つソリューションです
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;
}
これは、curlを追跡させるのではなく、リダイレクトチェーンを検査できるより詳細なバージョンです。
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
ヘッダーが混在しています。
xaav 答えはとても良いです。次の2つの問題を除いて:
一部のサイトは、基盤となるユーザーエージェント(クライアントブラウザ)を認識しないため、機能しません=>これは、ユーザーエージェントヘッダーフィールドを追加することで簡単に修正されます: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モバイルSafari/534.30\r\n ";
変更された答えは次のとおりです。
/**
* get_redirect_url()
* Gets the address that the provided URL redirects to,
* or FALSE if there's 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;
}
回答@xaavおよび@HoussemBDIOUIからのコードに追加:404エラーの場合とURLが応答なしの場合。その場合、get_final_url($url)
は文字列を返します: 'エラー:404が見つかりません'および 'エラー:応答なし'。
/**
* get_redirect_url()
* Gets the address that the provided URL redirects to,
* or FALSE if there's no redirect,
* or 'Error: No Responce',
* or 'Error: 404 Not Found'
*
* @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 'Error: No Responce';
$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 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.132 Safari/537.36\r\n";
$request .= "Connection: Close\r\n\r\n";
fwrite($sock, $request);
$response = '';
while (!feof($sock))
$response .= fread($sock, 8192);
fclose($sock);
if (stripos($response, '404 Not Found') !== false)
{
return 'Error: 404 Not Found';
}
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,
* or 'Error: No Responce'
* or 'Error: 404 Not Found',
*
* @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;
}
}