Каков наилучший способ проверить, существует ли URL в PHP?

Каков наилучший способ увидеть URL-адрес, а ответ не 404?

5 ответов

Решение

Ты можешь использовать get_headers($url)

Пример 2 из руководства:

<?php
// By default get_headers uses a GET request to fetch the headers. If you
// want to send a HEAD request instead, you can do so using a stream context:
stream_context_set_default(
    array(
        'http' => array(
            'method' => 'HEAD'
        )
    )
);
print_r(get_headers('http://example.com'));

// gives
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
)

Первый элемент массива будет содержать код статуса ответа HTTP. Вы должны разобрать это.

Обратите внимание, что get_headers Функция в примере выдаст запрос HTTP HEAD, что означает, что он не будет извлекать тело URL. Это более эффективно, чем использование запроса GET, который также возвращает тело.

Также обратите внимание, что при установке контекста по умолчанию любые последующие вызовы, использующие поток HTTP-контекста, теперь будут выдавать запросы HEAD. Поэтому не забудьте сбросить контекст по умолчанию, чтобы снова использовать GET, когда закончите.

PHP также предоставляет переменную $http_response_header

$http_response_header массив похож на get_headers() функция. При использовании обертки HTTP, $http_response_header будет заполнен заголовками ответа HTTP. $http_response_header будет создан в локальной области.

Если вы хотите загрузить содержимое удаленного ресурса, вам не нужно делать два запроса (один, чтобы узнать, существует ли ресурс, а другой - его извлечь), а только один. В этом случае используйте что-то вроде file_get_contents получить содержимое, а затем проверить заголовки из переменной.

Я использую эту функцию, поскольку она также проверяет и возвращает протокол URL-адреса, если он не найден.

$theUrl = 'google.com';

function isValidURL($url) { 
    $urlRegex = '@(http(s)?)?(://)?(([a-zA-Z])([-\w]+\.)+([^\s\.]+[^\s]*)+[^,.\s])@';
    if(preg_match($urlRegex, $url)){
        return preg_replace($urlRegex, "http$2://$4", $url);
    } else {
        return false;
    }
}

var_dump(isValidURL($theUrl));
public function isLink($url)
{
    $result = false;
    if (!filter_var($url, FILTER_VALIDATE_URL) === false) {
        $getHeaders = get_headers($url);
        $result = strpos($getHeaders[0], '200') !== false;
    }
    return $result;
}

@ Гордон - Вот более полная рутина библиотеки, основанная на вашем ответе. Он включает в себя некоторую предварительную проверку правильности URL-адреса, дополнительную обработку ошибок и анализ возвращаемых заголовков. Это также следует за любыми цепями перенаправления в течение разумного количества шагов.

class cLib {
    static $lasterror = 'No error set yet';
    /**
     * @brief See with a URL is valid - i.e. a page can be successfully retrieved from it without error
     * @param string $url The URL to be checked
     * @param int $nredirects The number of redirects check so far
     * @return boolean True if OK, false if the URL cannot be fetched
     */
    static function checkUrl($url, $nredirects = 0) {
        // First, see if the URL is sensible
        if (filter_var($url, FILTER_VALIDATE_URL) === false) {
            self::$lasterror = sprintf('URL "%s" did not validate', $url);
            return false;
        }
        // Now try to fetch it
        $headers = @get_headers($url);
        if ($headers == false) {
            $error = error_get_last();
            self::$lasterror = sprintf('URL "%s" could not be read: %s', $url, $error['message']);
            return false;
        }
        $status = $headers[0];
        $rbits = explode(' ', $status);
        if (count($rbits) < 2) {
            self::$lasterror = sprintf('Cannot parse status "%s" from URL "%s"', $status, $url);
            return false;
        }
        if (in_array($rbits[1], array(301, 302, 304, 307, 308))) {
            // This URL has been redirected. Follow the redirection chain
            foreach ($headers as $header) {
                if (cLib::startsWith($header, 'Location:')) {
                    if (++$nredirects > 10) {
                        self::$lasterror = sprintf('URL "%s" was redirected over 10 times: abandoned check', $url);
                        return false;
                    }
                    return self::checkUrl(trim(substr($header, strlen('Location:'))), $nredirects);
                }
            }
            self::$lasterror = sprintf('URL "%s" was redirected but location could not be identified', $url);
            return false;
        } 
        if ($rbits[1] != 200) {
            self::$lasterror = sprintf('URL "%s" returned status "%s"', $url, $status);
            return false;
        }
        return true;
    }
}

Извиняюсь перед @FranciscoLuz - если вы ожидаете ошибок, основанных на вводе данных пользователем, метод "@ and error_get_last" кажется мне совершенно разумным - я не вижу ничего более правильного в использовании set_error_handler.

Кстати, я не уверен, что мне следовало сделать это как редактирование ответа @ Гордона, а не как отдельный ответ. Может кто-нибудь посоветовать?

Способ, который я разработал, чтобы определить, действительно ли URL-адрес существует или нет, — это следующий скрипт. Его можно улучшить, более точно проанализировав возвращаемые ошибки. Там я выполнил простой возврат ошибки, оценив, что только URL-адреса с «не удалось разрешить хост» неверны.

      function URL_EXIST($pUrl)
{
    $etat = true;
    $ch = curl_init($pUrl);
    curl_setopt($ch, CURLOPT_FAILONERROR, true);
    curl_setopt($ch, CURLOPT_NOBODY, true);
    if (curl_exec($ch) === false)
    {
        $mes = strtolower(curl_error($ch));
        $cdt_wrong = preg_match('#could not resolve host#',$mes);
        $cdt_wrong |= preg_match('#404 not found#',$mes);
        if($cdt_wrong==true)
        {
            $etat = false;
        }
    }
    curl_close($ch);

    return $etat;
}

с некоторыми примерами работает хорошо

Другие вопросы по тегам