Web

Может ли PHP, используя cURL, получить заголовки и тело ответа за один запрос

Есть ли способ получить как заголовки, так и тело запроса cURL с помощью PHP? Я обнаружил, что этот вариант:

curl_setopt($ch, CURLOPT_HEADER, true);

собирается вернуть тело плюс заголовки, но затем мне нужно проанализировать его, чтобы получить тело. Есть ли способ получить и то, и другое более удобным (и безопасным) способом?

Обратите внимание, что для «единого запроса» я имею в виду отказ от отправки запроса HEAD до GET/POST.

 

Ответ 1

Пример кода:

$ch = curl_init();

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_HEADER, 1);

// ...

$response = curl_exec($ch);

// Затем, после вызова curl_exec:

$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);

$header = substr($response, 0, $header_size);

$body = substr($response, $header_size);

Предупреждение: этот код может быть ненадежным при использовании с прокси-серверами или при обработке определенных типов перенаправлений. 

 

Ответ 2

Ответ, предложенный выше, не совсем корректен.

Разделение на \r\n\r\n\n ненадежно, если включен CURLOPT_FOLLOWLOCATION или если сервер отвечает кодом 100.

Не все серверы соответствуют стандартам и передают только \n для новых строк.

Определение размера заголовков через CURLINFO_HEADER_SIZE также не всегда надежно, особенно при использовании прокси-серверов или в некоторых аналогичных сценариях перенаправления.

Наиболее корректным методом является использование CURLOPT_HEADERFUNCTION.

Вот очень чистый метод выполнения этой функции с использованием закрытий PHP. Он также преобразует все заголовки в нижний регистр для последовательной работы с разными серверами и версиями HTTP.

Эта версия будет сохранять дублирующиеся заголовки.

Этот код соответствует RFC822 и RFC2616, пожалуйста, не предлагайте правки для использования функций mb_ string, это неправильно!

$ch = curl_init();

$headers = [];

curl_setopt($ch, CURLOPT_URL, $url);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

// эта функция вызывается curl для каждого полученного заголовка

curl_setopt($ch, CURLOPT_HEADERFUNCTION,

  function($curl, $header) use (&$headers) {

    $len = strlen($header);

    $header = explode(':', $header, 2);

    if (count($header) < 2) // игнорируем недействительные заголовки

      return $len;

    $headers[strtolower(trim($header[0]))][] = trim($header[1]);

    return $len;

  }

);

$data = curl_exec($ch);

print_r($headers);



Ответ 3

Curl имеет встроенную опцию для этого, называемую CURLOPT_HEADERFUNCTION. Значение этой опции должно быть именем функции обратного вызова. Curl передаст заголовок (и только заголовок!) этой функции обратного вызова построчно (таким образом, функция будет вызываться для каждой строки заголовка, начиная с верхней части раздела заголовка). Затем ваша функция обратного вызова может делать с ним все что угодно (и должна вернуть количество байт данной строки). Вот проверенный рабочий код:

function HandleHeaderLine( $curl, $header_line ) {

    echo "<br>YEAH: ".$header_line; // или сделать что угодно

    return strlen($header_line);

}

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, "http://www.google.com");

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_HEADERFUNCTION, "HandleHeaderLine");

$body = curl_exec($ch); 

Вышеописанное работает со всем, с различными протоколами и прокси, и вам не нужно беспокоиться о размере заголовка или устанавливать множество различных опций curl.

P. S.: Чтобы обработать строки заголовка с помощью объектного метода, сделайте следующее:

curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$object, 'methodName'))

 

Ответ 4

Если вам конкретно нужен Content-Type, для его получения есть специальная опция cURL:

$ch = curl_init($url);

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec($ch);

$content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);

 

Ответ 5

Можно использовать следующий код:

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_VERBOSE, 1);

curl_setopt($ch, CURLOPT_HEADER, 1);

$parts = explode("\r\n\r\nHTTP/", $response);

$parts = (count($parts) > 1 ? 'HTTP/' : '').array_pop($parts);

list($headers, $body) = explode("\r\n\r\n", $parts, 2);

Работает с HTTP/1.1 100 Continue перед другими заголовками.

Если вам нужно работать с глючными серверами, которые посылают только LF вместо CRLF в качестве перевода строки, вы можете использовать preg_split следующим образом:

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_VERBOSE, 1);

curl_setopt($ch, CURLOPT_HEADER, 1);

$parts = preg_split("@\r?\n\r?\nHTTP/@u", $response);

$parts = (count($parts) > 1 ? 'HTTP/' : '').array_pop($parts);

list($headers, $body) = preg_split("@\r?\n\r?\n@u", $parts, 2);

 

Ответ 6

Это возвращает один массив, в котором данные разделены, а заголовки перечислены. Это работает на основе того, что CURL вернет кусок заголовков [ пустая строка ] данных.

curl_setopt($ch, CURLOPT_HEADER, 1); // нам нужно это, чтобы вернуть заголовки

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_VERBOSE, true);

// $output содержит выходную строку

$output = curl_exec($ch);

$lines   = explode("\n",$output);

$out     = array();

$headers = true;

foreach ($lines as $l){

    $l = trim($l);

    if ($headers && !empty($l)){

        if (strpos($l,'HTTP') !== false){

            $p = explode(' ',$l);

            $out['Headers']['Status'] = trim($p[1]);

        } else {

            $p = explode(':',$l);

            $out['Headers'][$p[0]] = trim($p[1]);

        }

    } elseif (!empty($l)) {

        $out['Data'] = $l;

    }

    if (empty($l)){

        $headers = false;

    }

}

 

 

Схожие статьи

Web

Как запустить php-скрипт как процесс-демон

Web

Преобразование сценария PHP в автономный исполняемый файл Windows

Web

Транзакции в PHP + MySQL

Web

Могу ли я расширить некоторый класс, используя более одного базового класса в PHP

×