web-dev-qa-db-fra.com

Un moyen facile de tester une URL pour 404 en PHP?

J'apprends moi-même quelques notions élémentaires et j'ai parfois constaté que les URL que je saisissais dans mon code retournent 404, ce qui gomme le reste de mon code.

J'ai donc besoin d'un test en haut du code pour vérifier si l'URL renvoie 404 ou non.

Cela semblerait être une tâche plutôt simple, mais Google ne me donne aucune réponse. Je m'inquiète de chercher le mauvais matériel.

Un blog m'a recommandé d'utiliser ceci:

$valid = @fsockopen($url, 80, $errno, $errstr, 30);

puis testez pour voir si $ valide s'il est vide ou non.

Mais je pense que l'URL qui me pose des problèmes contient une redirection, donc $ valid est à venir vide pour toutes les valeurs. Ou peut-être que je fais autre chose de mal.

J'ai également examiné une "requête principale", mais je n'ai pas encore trouvé d'exemples de code réels avec lesquels je peux jouer ou essayer.

Suggestions? Et qu'est-ce que c'est à propos de curl?

144
bflora

Si vous utilisez PHP curl bindings , vous pouvez vérifier le code d'erreur en utilisant curl_getinfo en tant que tel:

_$handle = curl_init($url);
curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);

/* Get the HTML or whatever is linked in $url. */
$response = curl_exec($handle);

/* Check for 404 (file not found). */
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
if($httpCode == 404) {
    /* Handle 404 here. */
}

curl_close($handle);

/* Handle $response here. */
_
261
strager

Si vous utilisez php5, vous pouvez utiliser:

$url = 'http://www.example.com';
print_r(get_headers($url, 1));

Alternativement avec php4, un utilisateur a contribué à:

/**
This is a modified version of code from "stuart at sixletterwords dot com", at 14-Sep-2005 04:52. This version tries to emulate get_headers() function at PHP4. I think it works fairly well, and is simple. It is not the best emulation available, but it works.

Features:
- supports (and requires) full URLs.
- supports changing of default port in URL.
- stops downloading from socket as soon as end-of-headers is detected.

Limitations:
- only gets the root URL (see line with "GET / HTTP/1.1").
- don't support HTTPS (nor the default HTTPS port).
*/

if(!function_exists('get_headers'))
{
    function get_headers($url,$format=0)
    {
        $url=parse_url($url);
        $end = "\r\n\r\n";
        $fp = fsockopen($url['Host'], (empty($url['port'])?80:$url['port']), $errno, $errstr, 30);
        if ($fp)
        {
            $out  = "GET / HTTP/1.1\r\n";
            $out .= "Host: ".$url['Host']."\r\n";
            $out .= "Connection: Close\r\n\r\n";
            $var  = '';
            fwrite($fp, $out);
            while (!feof($fp))
            {
                $var.=fgets($fp, 1280);
                if(strpos($var,$end))
                    break;
            }
            fclose($fp);

            $var=preg_replace("/\r\n\r\n.*\$/",'',$var);
            $var=explode("\r\n",$var);
            if($format)
            {
                foreach($var as $i)
                {
                    if(preg_match('/^([a-zA-Z -]+): +(.*)$/',$i,$parts))
                        $v[$parts[1]]=$parts[2];
                }
                return $v;
            }
            else
                return $var;
        }
    }
}

Les deux auraient un résultat similaire à:

Array
(
    [0] => HTTP/1.1 200 OK
    [Date] => Sat, 29 May 2004 12:28:14 GMT
    [Server] => Apache/1.3.27 (Unix)  (Red-Hat/Linux)
    [Last-Modified] => Wed, 08 Jan 2003 23:11:55 GMT
    [ETag] => "3f80f-1b6-3e1cb03b"
    [Accept-Ranges] => bytes
    [Content-Length] => 438
    [Connection] => close
    [Content-Type] => text/html
)

Par conséquent, vous pouvez simplement vérifier que la réponse de l'en-tête était OK, par exemple:

$headers = get_headers($url, 1);
if ($headers[0] == 'HTTP/1.1 200 OK') {
//valid 
}

if ($headers[0] == 'HTTP/1.1 301 Moved Permanently') {
//moved or redirect page
}

Codes et définitions du W3C

98
Asciant

Avec le code de strager, vous pouvez également vérifier le code CURLINFO_HTTP_CODE pour d’autres codes. Certains sites Web ne signalent pas un 404; ils redirigent simplement vers une page 404 personnalisée et renvoient 302 (redirection) ou quelque chose de similaire. J'ai utilisé cela pour vérifier si un fichier réel (par exemple, robots.txt) existait ou non sur le serveur. Clairement, ce type de fichier ne provoquerait pas de redirection s'il existait, mais s'il ne le faisait pas, il serait redirigé vers une page 404 qui, comme je l'ai dit précédemment, risque de ne pas contenir de code 404.

function is_404($url) {
    $handle = curl_init($url);
    curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);

    /* Get the HTML or whatever is linked in $url. */
    $response = curl_exec($handle);

    /* Check for 404 (file not found). */
    $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
    curl_close($handle);

    /* If the document has loaded successfully without any redirection or error */
    if ($httpCode >= 200 && $httpCode < 300) {
        return false;
    } else {
        return true;
    }
}
33
Aram Kocharyan

Comme suggéré par strager, envisagez d'utiliser cURL. Vous pouvez également être intéressé par la définition de CURLOPT_NOBODY avec curl_setopt pour ne pas télécharger la page entière (vous ne voulez que les en-têtes).

21
Beau Simensen

Si vous cherchez la solution la plus simple et celle que vous pouvez essayer en une fois sur php5 do

file_get_contents('www.yoursite.com');
//and check by echoing
echo $http_response_header[0];
15
Nasaralla

J'ai trouvé cette réponse ici :

if(($Twitter_XML_raw=file_get_contents($timeline))==false){
    // Retrieve HTTP status code
    list($version,$status_code,$msg) = explode(' ',$http_response_header[0], 3);

    // Check the HTTP Status code
    switch($status_code) {
        case 200:
                $error_status="200: Success";
                break;
        case 401:
                $error_status="401: Login failure.  Try logging out and back in.  Password are ONLY used when posting.";
                break;
        case 400:
                $error_status="400: Invalid request.  You may have exceeded your rate limit.";
                break;
        case 404:
                $error_status="404: Not found.  This shouldn't happen.  Please let me know what happened using the feedback link above.";
                break;
        case 500:
                $error_status="500: Twitter servers replied with an error. Hopefully they'll be OK soon!";
                break;
        case 502:
                $error_status="502: Twitter servers may be down or being upgraded. Hopefully they'll be OK soon!";
                break;
        case 503:
                $error_status="503: Twitter service unavailable. Hopefully they'll be OK soon!";
                break;
        default:
                $error_status="Undocumented error: " . $status_code;
                break;
    }

Vous utilisez essentiellement la méthode "file get contents" pour récupérer l'URL, qui renseigne automatiquement la variable d'en-tête de réponse http avec le code d'état.

6
Ross

addendum; testé ces 3 méthodes en considérant les performances.

Le résultat, au moins dans mon environnement de test:

victoire gagne

Ce test est effectué en considérant que seuls les en-têtes (noBody) sont nécessaires. Testez-vous:

$url = "http://de.wikipedia.org/wiki/Pinocchio";

$start_time = microtime(TRUE);
$headers = get_headers($url);
echo $headers[0]."<br>";
$end_time = microtime(TRUE);
echo $end_time - $start_time."<br>";


$start_time = microtime(TRUE);
$response = file_get_contents($url);
echo $http_response_header[0]."<br>";
$end_time = microtime(TRUE);
echo $end_time - $start_time."<br>";

$start_time = microtime(TRUE);
$handle = curl_init($url);
curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($handle, CURLOPT_NOBODY, 1); // and *only* get the header 
/* Get the HTML or whatever is linked in $url. */
$response = curl_exec($handle);
/* Check for 404 (file not found). */
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
// if($httpCode == 404) {
    // /* Handle 404 here. */
// }
echo $httpCode."<br>";
curl_close($handle);
$end_time = microtime(TRUE);
echo $end_time - $start_time."<br>";
4
Email

Cela vous donnera true si l'URL ne retourne pas 200 OK

function check_404($url) {
   $headers=get_headers($url, 1);
   if ($headers[0]!='HTTP/1.1 200 OK') return true; else return false;
}
2
Juergen

En guise d’allusion supplémentaire à la grande réponse acceptée:

Lors de l'utilisation d'une variante de la solution proposée, j'ai eu des erreurs en raison du paramètre php 'max_execution_time'. Donc ce que j'ai fait était le suivant:

set_time_limit(120);
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_NOBODY, true);
$result = curl_exec($curl);
set_time_limit(ini_get('max_execution_time'));
curl_close($curl);

Tout d'abord, je règle la limite de temps sur un nombre de secondes plus élevé, puis je le redéfinis sur la valeur définie dans les paramètres php.

2
markus
<?php

$url= 'www.something.com';
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, true);   
curl_setopt($ch, CURLOPT_NOBODY, true);    
curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US; rv:1.9.0.3) Gecko/2008092417 Firefox/3.0.4");
curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
curl_setopt($ch, CURLOPT_TIMEOUT,10);
curl_setopt($ch, CURLOPT_ENCODING, "gzip");
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
$output = curl_exec($ch);
$httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);


echo $httpcode;
?>
2

Vous pouvez aussi utiliser ce code pour voir le statut de n'importe quel lien:

<?php

function get_url_status($url, $timeout = 10) 
{
$ch = curl_init();
// set cURL options
$opts = array(CURLOPT_RETURNTRANSFER => true, // do not output to browser
            CURLOPT_URL => $url,            // set URL
            CURLOPT_NOBODY => true,         // do a HEAD request only
            CURLOPT_TIMEOUT => $timeout);   // set timeout
curl_setopt_array($ch, $opts);
curl_exec($ch); // do it!
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE); // find HTTP status
curl_close($ch); // close handle
echo $status; //or return $status;
    //example checking
    if ($status == '302') { echo 'HEY, redirection';}
}

get_url_status('http://yourpage.comm');
?>
1
T.Todua

Voici une solution courte.

$handle = curl_init($uri);
curl_setopt($handle,  CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($handle,CURLOPT_HTTPHEADER,array ("Accept: application/rdf+xml"));
curl_setopt($handle, CURLOPT_NOBODY, true);
curl_exec($handle);
$httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE);
if($httpCode == 200||$httpCode == 303) 
{
    echo "you might get a reply";
}
curl_close($handle);

Dans votre cas, vous pouvez remplacer application/rdf+xml par tout ce que vous utilisez.

1
Andreas

c'est juste et tranche de code, l'espoir fonctionne pour vous

            $ch = @curl_init();
            @curl_setopt($ch, CURLOPT_URL, 'http://example.com');
            @curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.1) Gecko/20061204 Firefox/2.0.0.1");
            @curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
            @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
            @curl_setopt($ch, CURLOPT_TIMEOUT, 10);

            $response       = @curl_exec($ch);
            $errno          = @curl_errno($ch);
            $error          = @curl_error($ch);

                    $response = $response;
                    $info = @curl_getinfo($ch);
return $info['http_code'];
0
gabriel

Pour attraper toutes les erreurs: 4XX et 5XX, j'utilise ce petit script:

function URLIsValid($URL){
    $headers = @get_headers($URL);
    preg_match("/ [45][0-9]{2} /", (string)$headers[0] , $match);
    return count($match) === 0;
}
0
wawan