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