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

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

curl_setopt($ch, CURLOPT_HEADER, true); 

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

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

Одно из решений этого было опубликовано в комментариях к документации по PHP: http://www.php.net/manual/en/function.curl-exec.php#80442

Пример кода:

 $ch = curl_init(); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_VERBOSE, 1); curl_setopt($ch, CURLOPT_HEADER, 1); // ... $response = curl_exec($ch); // Then, after your curl_exec call: $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE); $header = substr($response, 0, $header_size); $body = substr($response, $header_size); 

Предупреждение. Как отмечено в комментариях ниже, это может быть ненадежным при использовании с прокси-серверами или при обработке определенных типов перенаправлений. @ Ответ Джеффри может справиться с этим более надежно.

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

 function HandleHeaderLine( $curl, $header_line ) { echo "<br>YEAH: ".$header_line; // or do whatever 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); 

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

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

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

Многие другие решения, предлагаемые этой нитью, не делают это правильно.

  • Разделение на \r\n\r\n не является надежным, когда CURLOPT_FOLLOWLOCATION или когда сервер отвечает кодом 100.
  • Не все серверы соответствуют стандартам и передают только \n для новых строк.
  • Обнаружение размера заголовков через CURLINFO_HEADER_SIZE также не всегда является надежным, особенно когда используются прокси или в некоторых сценариях перенаправления.

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

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

Эта версия сохранит дублированные заголовки

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

 $ch = curl_init(); $headers = []; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // this function is called by curl for each header received curl_setopt($ch, CURLOPT_HEADERFUNCTION, function($curl, $header) use (&$headers) { $len = strlen($header); $header = explode(':', $header, 2); if (count($header) < 2) // ignore invalid headers return $len; $name = strtolower(trim($header[0])); if (!array_key_exists($name, $headers)) $headers[$name] = [trim($header[1])]; else $headers[$name][] = trim($header[1]); return $len; } ); $data = curl_exec($ch); print_r($headers); 

это то, что вы ищете?

 curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); $response = curl_exec($ch); list($header, $body) = explode("\r\n\r\n", $response, 2); 

Если вы специально хотите 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); 

Просто установите параметры:

  • CURLOPT_HEADER, 0

  • CURLOPT_RETURNTRANSFER, 1

и используйте curl_getinfo с CURLINFO_HTTP_CODE (или no opt param, и у вас будет ассоциативный массив со всей необходимой информацией)

Подробнее: http://php.net/manual/fr/function.curl-getinfo.php

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

Мой путь

 $response = curl_exec($ch); $x = explode("\r\n\r\n", $v, 3); $header=http_parse_headers($x[0]); if ($header=['Response Code']==100){ //use the other "header" $header=http_parse_headers($x[1]); $body=$x[2]; }else{ $body=$x[1]; } 

При необходимости примените цикл for и удалите ограничение на разрыв.

Проблема с множеством ответов здесь заключается в том, что "\r\n\r\n" может законно отображаться в теле html, поэтому вы не можете быть уверены, что правильно разделяете заголовки.

Кажется, что единственный способ хранения заголовков отдельно с одним вызовом curl_exec – использовать обратный вызов, как предлагается выше, в https://stackoverflow.com/a/25118032/3326494

И затем, чтобы (надежно) получить только тело запроса, вам нужно передать значение заголовка Content-Length в substr() как отрицательное начальное значение.

Будьте осторожны, когда вам понадобится последний материал, который возвращается с сервера. Этот код может нарушить ваше ожидание, ожидая реальных (последних) заголовков и body: list($headers, $body) = explode("\r\n\r\n", $result, 2);

Вот простой способ получить финальные заголовки и части тела;

 $result = explode("\r\n\r\n", $result); // drop redirect etc. headers while (count($result) > 2) { array_shift($result); } // split headers / body parts @ list($headers, $body) = $result; 

Возвращаемые заголовки ответа с эталонным параметром:

 <?php $data=array('device_token'=>'5641c5b10751c49c07ceb4', 'content'=>'测试测试test' ); $rtn=curl_to_host('POST', 'http://test.com/send_by_device_token', array(), $data, $resp_headers); echo $rtn; var_export($resp_headers); function curl_to_host($method, $url, $headers, $data, &$resp_headers) {$ch=curl_init($url); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $GLOBALS['POST_TO_HOST.LINE_TIMEOUT']?$GLOBALS['POST_TO_HOST.LINE_TIMEOUT']:5); curl_setopt($ch, CURLOPT_TIMEOUT, $GLOBALS['POST_TO_HOST.TOTAL_TIMEOUT']?$GLOBALS['POST_TO_HOST.TOTAL_TIMEOUT']:20); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, false); curl_setopt($ch, CURLOPT_HEADER, 1); if ($method=='POST') {curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data)); } foreach ($headers as $k=>$v) {$headers[$k]=str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', $k)))).': '.$v; } curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); $rtn=curl_exec($ch); curl_close($ch); $rtn=explode("\r\n\r\nHTTP/", $rtn, 2); //to deal with "HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK...\r\n\r\n..." header $rtn=(count($rtn)>1 ? 'HTTP/' : '').array_pop($rtn); list($str_resp_headers, $rtn)=explode("\r\n\r\n", $rtn, 2); $str_resp_headers=explode("\r\n", $str_resp_headers); array_shift($str_resp_headers); //get rid of "HTTP/1.1 200 OK" $resp_headers=array(); foreach ($str_resp_headers as $k=>$v) {$v=explode(': ', $v, 2); $resp_headers[$v[0]]=$v[1]; } return $rtn; } ?> 

Если вам действительно не нужно использовать завиток;

 $body = file_get_contents('http://example.com'); var_export($http_response_header); var_export($body); 

Какие результаты

 array ( 0 => 'HTTP/1.0 200 OK', 1 => 'Accept-Ranges: bytes', 2 => 'Cache-Control: max-age=604800', 3 => 'Content-Type: text/html', 4 => 'Date: Tue, 24 Feb 2015 20:37:13 GMT', 5 => 'Etag: "359670651"', 6 => 'Expires: Tue, 03 Mar 2015 20:37:13 GMT', 7 => 'Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT', 8 => 'Server: ECS (cpm/F9D5)', 9 => 'X-Cache: HIT', 10 => 'x-ec-custom-error: 1', 11 => 'Content-Length: 1270', 12 => 'Connection: close', )'<!doctype html> <html> <head> <title>Example Domain</title>... 

См. http://php.net/manual/en/reserved.variables.httpresponseheader.php