2010-09-26 5 views
12

Was ich tun möchte, ist herauszufinden, was ist die letzte/endgültige URL nach den Weiterleitungen folgen.Wie erhält man die endgültige URL, nachdem HTTP-Umleitungen in reinem PHP ausgeführt wurden?

Ich würde es vorziehen, cURL nicht zu verwenden. Ich würde gerne bei reinem PHP bleiben (Stream Wrapper).

Jetzt habe ich eine URL (sagen wir http://domain.test), und ich benutze get_headers(), um bestimmte Header von dieser Seite zu erhalten. get_headers gibt auch mehrere Location: Header zurück (siehe Edit unten). Gibt es eine Möglichkeit, diese Header zum Erstellen der finalen URL zu verwenden? oder gibt es eine PHP-Funktion, die dies automatisch tun würde?

Bearbeiten: get_headers() folgt Umleitungen und gibt alle Header für jede Antwort/Umleitungen zurück, so habe ich alle Location: Header.

+1

Es gibt * mehrere * 'Location:' Header in einer einzigen Antwort? – Tomalak

+0

get_headers folgt Umleitungen automatisch von defualt, so bekomme ich mehrere 'Location:' Header. Was ich möchte, ist die vollständige finale URL (http: //domain.test/final/page.ext? Attr ...) – Weboide

+0

Ich habe die Frage nicht verstanden :( – Stewie

Antwort

25
/** 
* 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; 
    } 
} 

Und, wie immer, gibt Kredit:

http://w-shadow.com/blog/2008/07/05/how-to-get-redirect-url-in-php/

+0

Sie Sir, rettete mir nur ein paar Stunden zu suchen. Alles funktioniert wie erwartet. – Dave

+0

Ich muss sagen, dass diese Lösung mit CURL für meine Tests zuverlässiger ist: http://stackoverflow.com/questions/17472329/php-get-url-of-redirect-from-source-url –

36
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; 
} 

Zusätzlich ...

Wie in einem Kommentar erwähnt, die letzten Artikel in $headers['Location'] wird b Geben Sie Ihre endgültige URL nach allen Weiterleitungen ein. Es ist jedoch wichtig zu beachten, dass es kein Array immer sein wird. Manchmal ist es nur eine gewöhnliche Variable ohne Array. In diesem Fall wird beim Versuch, auf das letzte Array-Element zuzugreifen, höchstwahrscheinlich ein einzelnes Zeichen zurückgegeben. Nicht ideal.

Wenn Sie daran interessiert sind nur in der letzten URL sind, nachdem alle Umleitungen, würde ich

return $headers['Location']; 

zu

return is_array($headers['Location']) ? array_pop($headers['Location']) : $headers['Location']; 

Wechsel vorschlagen ... die nur if short-hand für

ist
if(is_array($headers['Location'])){ 
    return array_pop($headers['Location']); 
}else{ 
    return $headers['Location']; 
} 

Dieses Update wird auf jeden Fall (Array, Nicht-Array) kümmern, und entfernen Sie die Notwendigkeit, wir die finale URL nach dem Aufruf der Funktion ausgegeben.

In dem Fall, in dem keine Weiterleitungen vorhanden sind, gibt die Funktion false zurück. In ähnlicher Weise gibt die Funktion auch false für ungültige URLs zurück (aus irgendeinem Grund ungültig). Daher ist es wichtig, check the URL for validityvor diese Funktion ausführen, oder sonst die Redirect-Check irgendwo in Ihre Validierung einbeziehen.

+0

Enthält diese folgen alle Umleitungen und gibt die endgültige URL zurück? – Weboide

+1

Super! Dies verdient mehr Upvotes. – Ashfame

+1

Funktioniert gut! +1 – user327843

3

xaav Antwort ist sehr gut; mit Ausnahme der folgenden zwei Fragen:

  • Es HTTPS-Protokoll => Die Lösung wurde als Kommentar in der Original-Website vorgeschlagen nicht unterstützt: http://w-shadow.com/blog/2008/07/05/how-to-get-redirect-url-in-php/
  • Einige Seiten funktionieren nicht, da sie nicht erkennen die zugrunde liegenden User-Agent (Client-Browser) => Diese einfach durch Hinzufügen eines User-Agent-Header-Feld festgelegt ist: ich habe ein Android-User-Agent (können Sie hier http://www.useragentstring.com/pages/useragentstring.php anderen User-Agent-Beispiele finden Sie Ihren Bedarf nach):

    $ request . = "Benutzer-Agent: Mozilla/5.0 (Linux; U; Android 4.0.3; ko-kr; LG-L160L Aufbau/IML7 4K) AppleWebkit/534.30 (KHTML, wie Gecko) Version/4.0 Mobile Safari/534.30 \ r \ n ";

Hier ist die modifizierte Antwort:

/** 
* 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; 
} 
+0

Fehler 500 beim Ausführen dieses Skripts. –

+0

Können Sie die Fehlermeldung angeben? –

2

Während der OP vermeiden cURL wollte, ist es am besten, es zu benutzen, wenn es verfügbar ist. Hier ist eine Lösung, die die folgenden Vorteile

  • verwendet curl für all schweres Heben, arbeitet so mit https
  • meistert mit Servern, die niedrigen Rück verrohrten location Header-Namen (sowohl xaav und Webjay der Antworten nicht damit umgehen)
  • können Sie steuern, wie tief du dich, bevor er aufgibt
  • hinwollen

Hier ist die Funktion:

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; 
} 

Hier ist eine ausführlichere Version, mit der Sie die Redirection-Kette überprüfen können, anstatt Curl folgen zu lassen.

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; 
} 

Als Beispiel für die Umleitung Kette, die diese Funktion übernimmt, aber die andere nicht, versuchen Sie dies:

echo findUltimateDestination('http://dx.doi.org/10.1016/j.infsof.2016.05.005') 

Zum Zeitpunkt des Schreibens beinhaltet diese 4-Anfragen, mit einer Mischung aus Location und location Header beteiligt.

Verwandte Themen