Проверка доступности веб-ссылок с помощью PHP: полное руководство с примерами

Раздел: Веб-разработка на PHP -> Работа с URL и редиректами

Проверка работоспособности ссылок в PHP

Как выполнить массовую проверку ссылок с максимальной производительностью?

Наиболее эффективное решение для проверки большого количества URL - использование функций curl_multi (многопоточный cURL). Оно позволяет отправлять несколько запросов одновременно, экономя время и ресурсы сервера.

Пример базовой реализации:

$urls = [
    'https://example.com',
    'https://httpbin.org/status/200',
    'https://httpbin.org/status/404'
];

$mh = curl_multi_init();
$channels = [];

foreach ($urls as $id => $url) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HEADER => false,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_CONNECTTIMEOUT => 5,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_NOBODY => true // HEAD-запрос для экономии трафика
    ]);
    curl_multi_add_handle($mh, $ch);
    $channels[$id] = $ch;
}

$active = null;
do {
    $status = curl_multi_exec($mh, $active);
    if ($status !== CURLM_OK) break;
    curl_multi_select($mh, 0.5); // ожидание активности
} while ($active > 0);

$results = [];
foreach ($channels as $id => $ch) {
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $error = curl_error($ch);
    $results[$id] = [
        'url' => $urls[$id],
        'http_code' => $httpCode,
        'ok' => ($httpCode >= 200 && $httpCode < 400),
        'error' => $error ?: null
    ];
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}
curl_multi_close($mh);

print_r($results);

Php проверка ссылок (проверка работоспособности ссылок в php)

Типичные проблемы и их решения:

  • Таймауты соединения: увеличивайте CURLOPT_TIMEOUT и CURLOPT_CONNECTTIMEOUT.
  • Проблемы с SSL: установите CURLOPT_SSL_VERIFYPEER = false (только для тестовой среды).
  • Редиректы: используйте CURLOPT_FOLLOWLOCATION с ограничением CURLOPT_MAXREDIRS.
  • Ложные 200: некоторые сайты возвращают 200 на страницы ошибок - проверяйте тело ответа или заголовок Content-Length.
  • Ограничение памяти: при проверке тысяч ссылок используйте пакетную обработку (чанки) и освобождайте ресурсы после каждого пакета.

Как проверить одну ссылку с помощью file_get_contents?

Самый простой способ - использовать встроенную функцию file_get_contents с контекстом потока для отправки HTTP запроса. Подходит для быстрой проверки единичного URL, но малоэффективен для массовых задач из-за последовательного выполнения.

$url = 'https://example.com';
$options = [
    'http' => [
        'method' => 'HEAD',
        'timeout' => 10,
        'ignore_errors' => true // не генерировать warning при ошибках HTTP
    ]
];
$context = stream_context_create($options);
$result = @file_get_contents($url, false, $context);
if ($result === false) {
    echo "Не удалось открыть $url";
} else {
    echo "Ссылка доступна";
}

Проблема: функция не даёт получить HTTP-код ответа без дополнительных ухищрений. Для получения кода нужно использовать $http_response_header или обёртку через stream_get_meta_data.

$result = @file_get_contents($url, false, $context);
if (isset($http_response_header[0])) {
    preg_match('/HTTP\/[\d.]+\s+(\d+)/', $http_response_header[0], $matches);
    $httpCode = (int)$matches[1];
    echo "HTTP код: $httpCode";
}

Когда используется: для единичных проверок, в окружениях без cURL, для очень простых скриптов.

Как проверить ссылку с помощью cURL (одиночный запрос)?

Функции curl_init и curl_exec предоставляют полный контроль над запросом. Данный подход удобен для проверки с тонкой настройкой (таймауты, заголовки, аутентификация).

$url = 'https://httpbin.org/status/200';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_NOBODY => true,
    CURLOPT_TIMEOUT => 10,
    CURLOPT_FOLLOWLOCATION => true
]);
curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$error = curl_error($ch);
curl_close($ch);

$isOk = ($httpCode >= 200 && $httpCode < 400);
echo "URL: $url, HTTP: $httpCode, Статус: " . ($isOk ? 'Работает' : 'Не работает');
if ($error) echo " Ошибка: $error";

Проблемы: медленная последовательная проверка при большом количестве URL, возможные утечки памяти при неправильном закрытии дескрипторов.

Когда используется: для одиночных проверок, отладки, интеграции с другими модулями.

Как проверить ссылку с помощью fsockopen (низкоуровнево)?

Функция fsockopen позволяет открыть сокет и вручную отправлять HTTP-запросы. Подходит для случаев, когда нет ни cURL, ни file_get_contents с контекстом.

$host = 'example.com';
$port = 443;
$path = '/';
$timeout = 10;

$fp = @fsockopen('ssl://' . $host, $port, $errno, $errstr, $timeout);
if (!$fp) {
    echo "Ошибка соединения: $errstr ($errno)";
} else {
    $request = "HEAD $path HTTP/1.1\r\nHost: $host\r\nConnection: close\r\n\r\n";
    fwrite($fp, $request);
    $response = fgets($fp);
    fclose($fp);
    // $response содержит первую строку ответа, например HTTP/1.1 200 OK
    echo $response;
}

Проблемы: сложность обработки редиректов, SSL-рукопожатий, необходимость вручную парсить заголовки. Не рекомендуется для продакшена.

Когда используется: в специфических средах (например, встроенный PHP без расширений) или для образовательных целей.

Как выполнить асинхронную проверку ссылок с помощью Guzzle?

Библиотека Guzzle предоставляет мощные асинхронные возможности для отправки множества запросов параллельно. Это современная альтернатива curl_multi с более удобным API.

require 'vendor/autoload.php';

use GuzzleHttp\Client;
use GuzzleHttp\Promise;

$client = new Client([
    'timeout' => 10,
    'allow_redirects' => ['max' => 5]
]);

$urls = [
    'https://example.com',
    'https://httpbin.org/status/404'
];

$promises = [];
foreach ($urls as $id => $url) {
    $promises[$id] = $client->headAsync($url);
}

$responses = Promise\settle($promises)->wait();

foreach ($responses as $id => $result) {
    $state = $result['state'];
    if ($state === 'fulfilled') {
        $code = $result['value']->getStatusCode();
        echo "URL: {$urls[$id]} - HTTP $code - " . ($code >= 200 && $code < 400 ? 'OK' : 'FAIL') . PHP_EOL;
    } else {
        echo "URL: {$urls[$id]} - Ошибка: {$result['reason']}" . PHP_EOL;
    }
}

Проблемы: требуется установка Composer и библиотеки, повышенное потребление памяти при очень большом количестве запросов (можно использовать пакетную обработку с Pool).

Когда используется: в современных проектах с Composer, где удобно иметь объектно-ориентированный интерфейс и асинхронность «из коробки».

Как обрабатывать редиректы при проверке ссылок?

Редиректы могут влиять на итоговый статус. Важно либо следовать за ними, либо анализировать заголовок Location. curl автоматически обрабатывает редиректы при установке CURLOPT_FOLLOWLOCATION. Для ручной обработки редиректов можно получить итоговый URL через curl_getinfo($ch, CURLINFO_EFFECTIVE_URL).

$ch = curl_init('https://httpbin.org/redirect-to?url=https://example.com');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_NOBODY => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_MAXREDIRS => 5
]);
curl_exec($ch);
$finalUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
echo "Конечный URL: $finalUrl";

Когда используется: когда нужно проверить не только доступность исходной ссылки, но и её финальное назначение (например, поиск битых редиректов).

Как проверить ссылку с пользовательским User-Agent и заголовками?

Некоторые сайты блокируют запросы без идентификации. Установка User-Agent и других заголовков помогает пройти такие блокировки.

$ch = curl_init('https://example.com');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'User-Agent: Mozilla/5.0 (compatible; Bot/1.0)',
        'Accept: text/html',
    ],
    CURLOPT_NOBODY => true,
    CURLOPT_TIMEOUT => 10
]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo "Страница отвечает кодом $code";

Когда используется: для проверки ссылок за фаерволом или с защитой от ботов.

Расширенные примеры проверки ссылок

Ниже приведены нестандартные, но полезные сценарии с подробным кодом и результатами.

Пример 1. Проверка ссылок с получением времени отклика

Пример
function checkUrlWithTiming($url) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_NOBODY => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_CONNECTTIMEOUT => 5
    ]);
    $start = microtime(true);
    curl_exec($ch);
    $totalTime = microtime(true) - $start;
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $connectTime = curl_getinfo($ch, CURLINFO_CONNECT_TIME);
    curl_close($ch);
    return [
        'url' => $url,
        'http_code' => $httpCode,
        'total_time' => round($totalTime, 3),
        'connect_time' => round($connectTime, 3)
    ];
}

// Пример вызова:
$result = checkUrlWithTiming('https://google.com');
print_r($result);
/* Результат:
Array
(
    [url] => https://google.com
    [http_code] => 200
    [total_time] => 0.425
    [connect_time] => 0.098
)
*/

Пример 2. Пакетная проверка с ограничением параллельных соединений (curl_multi + лимит)

Пример
$allUrls = [/* массив из 100 URL */];
$batchSize = 10; // не более 10 одновременных запросов
$results = [];

for ($i = 0; $i < count($allUrls); $i += $batchSize) {
    $batch = array_slice($allUrls, $i, $batchSize);
    
    $mh = curl_multi_init();
    $channels = [];
    foreach ($batch as $id => $url) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_NOBODY => true,
            CURLOPT_TIMEOUT => 10
        ]);
        curl_multi_add_handle($mh, $ch);
        $channels[$id] = $ch;
    }
    
    $active = null;
    do {
        $status = curl_multi_exec($mh, $active);
        curl_multi_select($mh);
    } while ($active > 0);
    
    foreach ($channels as $id => $ch) {
        $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $results[] = [
            'url' => $batch[$id],
            'http_code' => $code,
            'ok' => $code >= 200 && $code < 400
        ];
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }
    curl_multi_close($mh);
}

echo "Проверено: " . count($results) . " URL";
/* Результат: массив результатов для каждого URL */

Пример 3. Проверка ссылок с авторизацией (Basic Auth)

Пример
$url = 'https://httpbin.org/basic-auth/user/pass';
$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_NOBODY => true,
    CURLOPT_USERPWD => 'user:pass',
    CURLOPT_HTTPAUTH => CURLAUTH_BASIC
]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo "HTTP код: $code"; // 200 при верных учётных данных, 401 иначе

Пример 4. Обработка относительных ссылок и преобразование в абсолютные

Пример
function resolveUrl($base, $relative) {
    $base = rtrim($base, '/');
    if (preg_match('#^https?://#i', $relative)) {
        return $relative;
    }
    if (strpos($relative, '/') === 0) {
        $parsed = parse_url($base);
        return $parsed['scheme'] . '://' . $parsed['host'] . $relative;
    }
    // иначе конкатенация
    return $base . '/' . ltrim($relative, '/');
}

$baseUrl = 'https://example.com/some/page';
$relative = '../other/page.html';
$absolute = resolveUrl($baseUrl, $relative);
echo $absolute;
// Результат: https://example.com/other/page.html

Пример 5. Использование HEAD-запроса с fallback на GET

Пример
function checkUrlSmart($url) {
    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_NOBODY => true,
        CURLOPT_TIMEOUT => 10,
        CURLOPT_FOLLOWLOCATION => true
    ]);
    $result = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    
    // Если HEAD не поддерживается (405 Method Not Allowed), пробуем GET
    if ($httpCode == 405) {
        curl_setopt($ch, CURLOPT_NOBODY, false);
        curl_setopt($ch, CURLOPT_RANGE, '0-0'); // запросить только первый байт
        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    }
    curl_close($ch);
    return $httpCode;
}

echo checkUrlSmart('https://httpbin.org/status/405'); // может вернуть 405 или 200

Пример 6. Проверка ссылок с прокси (для обхода блокировок)

Пример
$ch = curl_init('https://example.com');
curl_setopt_array($ch, [
    CURLOPT_PROXY => 'http://proxy.example.com:8080',
    CURLOPT_PROXYUSERPWD => 'user:pass',
    CURLOPT_PROXYTYPE => CURLPROXY_HTTP,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_NOBODY => true
]);
curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
echo "Через прокси: код $code";
curl_close($ch);

Пример 7. Получение заголовков ответа для глубокого анализа

Пример
$ch = curl_init('https://example.com');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HEADER => true,
    CURLOPT_NOBODY => false,
    CURLOPT_TIMEOUT => 10
]);
$response = curl_exec($ch);
$headerSize = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$headers = substr($response, 0, $headerSize);
$body = substr($response, $headerSize);
curl_close($ch);

echo "Заголовки:\n$headers";
echo "Тело (первые 100 символов): " . substr($body, 0, 100);

проверка работоспособности ссылок в PHP - comments

En
Php проверка ссылок (php)