是否有任何方法来获得头部和主体的cURL请求使用PHP?我发现这个选项:

curl_setopt($ch, CURLOPT_HEADER, true);

会返回body和header,但之后我需要解析它来得到body。有没有办法以更可用(和更安全)的方式同时获得两者?

注意,对于“单个请求”,我的意思是避免在GET/POST之前发出HEAD请求。


当前回答

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

上面的工作与一切,不同的协议和代理,你不需要担心头部的大小,或设置许多不同的卷曲选项。

注:要用object方法处理标题行,请这样做:

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

其他回答

这个线程提供的许多其他解决方案都没有正确地做到这一点。

当CURLOPT_FOLLOWLOCATION打开或服务器响应100代码RFC-7231, MDN时,在\r\n\r\n上的分裂是不可靠的。 并不是所有的服务器都符合标准,对新行只传输\n(收件人可能会丢弃行结束符中的\r)问答。 通过CURLINFO_HEADER_SIZE检测报头的大小也并不总是可靠的,特别是当代理使用Curl-1204或在某些相同的重定向场景中。

最正确的方法是使用CURLOPT_HEADERFUNCTION。

下面是使用PHP闭包执行此操作的一种非常简洁的方法。它还将所有头部转换为小写,以便跨服务器和HTTP版本进行一致的处理。

这个版本将保留重复的头文件

这符合RFC822和RFC2616,请不要使用mb_(和类似的)字符串函数,这不仅是不正确的,甚至是一个安全问题RFC-7230!

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

我的方法是

$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循环并移除爆炸限制。

更好的方法是使用详细的CURL响应,可以通过管道连接到临时流。然后可以在响应中搜索报头名称。这可能需要一些调整,但对我来说很管用:

class genericCURL {
    /**
     * NB this is designed for getting data, or for posting JSON data
     */
    public function request($url, $method = 'GET', $data = array()) {
        $ch = curl_init();
        
        if($method == 'POST') {
            
            curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
            curl_setopt($ch, CURLOPT_POSTFIELDS, $string = json_encode($data));
            
        }

        
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_VERBOSE, true);
        
        //open a temporary stream to output the curl log, which would normally got to STDERR
        $err = fopen("php://temp", "w+");
        curl_setopt($ch, CURLOPT_STDERR, $err);
        

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $server_output = curl_exec ($ch);
        
        //rewind the temp stream and put it into a string   
        rewind($err);
        $this->curl_log = stream_get_contents($err);
        
        curl_close($ch);
        fclose($err);

    
        return $server_output;
        
    }
    
    /**
     * use the curl log to get a header value
     */
    public function getReturnHeaderValue($header) {
        $log = explode("\n", str_replace("\r\n", "\n", $this->curl_log));
        foreach($log as $line) {
            //is the requested header there
            if(stripos($line, '< ' . $header . ':') !== false) {
                $value = trim(substr($line, strlen($header) + 3));
                return $value;
            }
        }
        //still here implies not found so return false
        return false;
        
    }
}

这里的许多答案的问题是“\r\n\r\n”可以合法地出现在html的主体中,因此您不能确保正确地分割了头文件。

在调用curl_exec时单独存储标头的唯一方法似乎是使用回调,就像上面https://stackoverflow.com/a/25118032/3326494中建议的那样

然后,为了(可靠地)获得请求的主体,您需要将Content-Length报头的值作为负起始值传递给substr()。

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

警告:正如下面的评论所指出的,当与代理服务器一起使用或在处理某些类型的重定向时,这可能不可靠。@Geoffrey的回答可能会更可靠地处理这些问题。