Maneira fácil de testar uma URL para 404 em PHP?

152

Estou aprendendo algumas coisas básicas e descobri que, às vezes, os URLs que eu alimento no meu código retornam 404, o que agrega todo o resto do meu código.

Então, eu preciso de um teste na parte superior do código para verificar se o URL retorna 404 ou não.

Isso parece uma tarefa bastante direta, mas o Google não está me dando respostas. Preocupo-me por procurar as coisas erradas.

Um blog recomendado eu uso isso:

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

e então teste para ver se $ válido se vazio ou não.

Mas acho que o URL que está me causando problemas tem um redirecionamento, portanto $ valid está vazio para todos os valores. Ou talvez eu esteja fazendo outra coisa errada.

Também examinei uma "solicitação de cabeçalho", mas ainda não encontrei exemplos de código reais com os quais possa brincar ou experimentar.

Sugestões? E o que é isso sobre curl?

nariz grande
fonte

Respostas:

276

Se você estiver usando as curlligações do PHP , poderá verificar o código de erro usando curl_getinfo:

$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. */
strager
fonte
1
Ainda não estou familiarizado com o cURL, por isso estou perdendo alguns conceitos. O que faço com a variável $ response abaixo? O que contém?
1
@ Bflora, eu cometi um erro no código. (Isso será corrigido em um segundo.) Você pode ver a documentação do curl_exec no site do PHP.
Strager #
4
A resposta @bflora $ conterá o conteúdo do $ url, para que você possa executar outras ações, como verificar o conteúdo por sequências específicas ou o que for. No seu caso, você se preocupa apenas com o estado 404, portanto provavelmente não precisa se preocupar com a resposta $.
Beau Simensen 03/01/09
5
E se você quiser carregar apenas os cabeçalhos em vez de baixar o arquivo inteiro?
patrick
13
@patrick então você precisa especificar curl_setopt($handle, CURLOPT_NOBODY, true);antes de executarcurl_exec
usuário
101

Se você estiver executando o php5, poderá usar:

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

Como alternativa ao php4, um usuário contribuiu com o seguinte:

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

Ambos teriam um resultado semelhante a:

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
)

Portanto, você pode verificar se a resposta do cabeçalho está correta, por exemplo:

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

Códigos e Definições W3C

Asciant
fonte
Fiz algumas melhorias na formatação da sua resposta e também adicionei a capacidade para https: get_headers($https_url,1,443);tenho certeza que funcionará, embora não esteja na get_headers()função padrão . Fique à vontade para testá-lo e responder com um status.
JamesM-SiteGen
1
boa solução alternativa para php4, mas para casos como este, temos o método http HEAD.
vidstige
Então isso seria realmente mais rápido que o método curl?
FLY
4
Esta solução não é válida quando o URL de destino é redirecionado para 404. Nesse caso, $ headers [0] será um código de redirecionamento, e o código 404 final será anexado em algum momento mais tarde na matriz de retorno.
roomcays
1
Isso acaba sendo mais problemático do que vale a pena no php para filtrar o código real da string resultante, ao tentar simplesmente lidar com o código de status em um script, em vez de ecoar o resultado para leitura.
Kzqai
37

Com o código do strager, você também pode verificar o CURLINFO_HTTP_CODE para outros códigos. Alguns sites não relatam um 404, eles simplesmente redirecionam para uma página 404 personalizada e retornam 302 (redirecionamento) ou algo semelhante. Eu usei isso para verificar se um arquivo real (por exemplo, robots.txt) existia no servidor ou não. Claramente, esse tipo de arquivo não causaria um redirecionamento se existisse, mas, se não existisse, seria redirecionado para uma página 404, que, como eu disse antes, pode não ter um código 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;
    }
}
Aram Kocharyan
fonte
5
+1 para o uso de "sucesso" HTTP códigos em vez de 404 ... O usuário pode obter um 408 Request Timeout, não um404
Guillaume
Funcionou como um encanto. Eu uso isso para verificar se um artigo no ebay ainda está online.
Nerdkowski
Para aqueles que esperam que o código acima funcione com https, tente adicionar o seguinte:curl_setopt($handle, CURLOPT_SSL_VERIFYPEER, FALSE); curl_setopt($handle, CURLOPT_SSL_VERIFYHOST, FALSE);
Kirk Hammett
mas isso também retornaria 404 = true se houver um redirecionamento 302 legítimo?
Robert Sinclair
22

Como o strager sugere, veja como usar cURL. Você também pode estar interessado em definir CURLOPT_NOBODY com curl_setopt para pular o download da página inteira (você só quer os cabeçalhos).

Beau Simensen
fonte
1
+1 por me mencionar ^ W ^ fornecendo uma alternativa mais eficiente, no caso em que apenas o cabeçalho precisa ser verificado. =]
strager
16

Se você está procurando uma solução mais fácil e a que você pode tentar de uma só vez no php5 do

file_get_contents('www.yoursite.com');
//and check by echoing
echo $http_response_header[0];
Nasaralla
fonte
3
btw, se estiver fazendo isso e o URL 404, um aviso será gerado, causando saída.
Chris K
mais fácil de fazer $ isExists = @file_get_contents ('www.yoursite.com'); if ($ isExists == verdadeiros!) {echo "yields 404"}
Tebe
colocar em um try catch, então lidar com a 404 com fecho
Garet Claborn
7

Encontrei esta resposta aqui :

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

Essencialmente, você usa o método "file get contents" para recuperar a URL, que preenche automaticamente a variável de cabeçalho de resposta http com o código de status.

Ross
fonte
2
Interessante - eu nunca tinha ouvido falar dessa magia global antes. php.net/manual/en/reserved.variables.httpresponseheader.php #
Frank Farmer
2
ironia - o link é 404
Hamzah Malik 26/08
6

Isso será verdadeiro se o URL não retornar 200 OK

function check_404($url) {
   $headers=get_headers($url, 1);
   if ($headers[0]!='HTTP/1.1 200 OK') return true; else return false;
}
Juergen
fonte
Isso é muito mais rápido do que usar cURL, se você quiser fazer uma simples verificação de bool em um URL. Obrigado.
Drmzindec 6/05/19
5

adendo; testou esses três métodos considerando o desempenho.

O resultado, pelo menos no meu ambiente de teste:

Ganhos de cachos

Este teste é realizado sob a consideração de que apenas os cabeçalhos (noBody) são necessários. Teste-se:

$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>";
O email
fonte
3

Como uma dica adicional para a ótima resposta aceita:

Ao usar uma variação da solução proposta, obtive erros devido à configuração do php 'max_execution_time'. Então, o que eu fiz foi o seguinte:

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

Primeiro, defino o limite de tempo para um número maior de segundos, no final, defino-o novamente para o valor definido nas configurações de php.

markus
fonte
hhhmmmm ... além disso ... seu código consome menos recursos porque você não está retornando o conteúdo ... ainda que você possa adicionar a transferência de retorno para false, pode economizar muitos recursos quando pessoas que usam várias chamadas ... iniciantes não pensam muito e por isso a razão para 40 os votos ... isso é bem ...
Jayapal Chandran
3
<?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;
?>
Melbin Mathew Antony
fonte
3

Aqui está uma solução curta.

$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);

No seu caso, você pode mudar application/rdf+xmlpara o que usar.

Andreas
fonte
2

Esta função retorna o código de status de uma URL no PHP 7:

/**
 * @param string $url
 * @return int
 */
function getHttpResponseCode(string $url): int
{
    $headers = get_headers($url);
    return substr($headers[0], 9, 3);
}

Exemplo:

echo getHttpResponseCode('https://www.google.com');
//displays: 200
Sebastian Viereck
fonte
1

Você também pode usar esse código para ver o status de qualquer link:

<?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');
?>
T.Todua
fonte
0

isso é justo e parte do código, a esperança funciona para você

            $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'];

fonte
0

Aqui está uma maneira!

<?php

$url = "http://www.google.com";

if(@file_get_contents($url)){
echo "Url Exists!";
} else {
echo "Url Doesn't Exist!";
}

?>

Esse script simples simplesmente faz uma solicitação ao URL para seu código-fonte. Se a solicitação for concluída com êxito, ela exibirá "URL Exists!". Caso contrário, ele exibirá "URL Não Existe!".

Hayden Frobenius
fonte