Can PHP cURL retrieve response headers AND body in

2020-01-22 11:55发布

Is there any way to get both headers and body for a cURL request using PHP? I found that this option:

curl_setopt($ch, CURLOPT_HEADER, true);

is going to return the body plus headers, but then I need to parse it to get the body. Is there any way to get both in a more usable (and secure) way?

Note that for "single request" I mean avoiding issuing a HEAD request prior of GET/POST.

标签: php http curl
13条回答
虎瘦雄心在
2楼-- · 2020-01-22 11:58

Curl has a built in option for this, called CURLOPT_HEADERFUNCTION. The value of this option must be the name of a callback function. Curl will pass the header (and the header only!) to this callback function, line-by-line (so the function will be called for each header line, starting from the top of the header section). Your callback function then can do anything with it (and must return the number of bytes of the given line). Here is a tested working code:

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

The above works with everything, different protocols and proxies too, and you dont need to worry about the header size, or set lots of different curl options.

P.S.: To handle the header lines with an object method, do this:

curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$object, 'methodName'))
查看更多
一纸荒年 Trace。
3楼-- · 2020-01-22 12:00

Return response headers with a reference parameter:

<?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;
         }
?>
查看更多
劫难
4楼-- · 2020-01-22 12:03

Many of the other solutions offered this thread are not doing this correctly.

  • Splitting on \r\n\r\n is not reliable when CURLOPT_FOLLOWLOCATION is on or when the server responds with a 100 code.
  • Not all servers are standards compliant and transmit just a \n for new lines.
  • Detecting the size of the headers via CURLINFO_HEADER_SIZE is also not always reliable, especially when proxies are used or in some of the same redirection scenarios.

The most correct method is using CURLOPT_HEADERFUNCTION.

Here is a very clean method of performing this using PHP closures. It also converts all headers to lowercase for consistent handling across servers and HTTP versions.

This version will retain duplicated headers

This complies with RFC822 and RFC2616, please do not suggest edits to make use of the mb_ string functions, it is incorrect!

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

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

    return $len;
  }
);

$data = curl_exec($ch);
print_r($headers);
查看更多
Explosion°爆炸
5楼-- · 2020-01-22 12:03

My way is

$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];
}

If needed apply a for loop and remove the explode limit.

查看更多
祖国的老花朵
6楼-- · 2020-01-22 12:03

If you don't really need to use curl;

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

Which outputs

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>...

See http://php.net/manual/en/reserved.variables.httpresponseheader.php

查看更多
萌系小妹纸
7楼-- · 2020-01-22 12:08

The problem with many answers here is that "\r\n\r\n" can legitimately appear in the body of the html, so you can't be sure that you're splitting headers correctly.

It seems that the only way to store headers separately with one call to curl_exec is to use a callback as is suggested above in https://stackoverflow.com/a/25118032/3326494

And then to (reliably) get just the body of the request, you would need to pass the value of the Content-Length header to substr() as a negative start value.

查看更多
登录 后发表回答