Есть ли способ получить как заголовки, так и тело запроса 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