PHP cURL可以在单个请求中检索响应标头和正文吗?

gre*_*emo 302 php curl http

有没有办法使用PHP获取cURL请求的标题和正文?我发现这个选项:

curl_setopt($ch, CURLOPT_HEADER, true);
Run Code Online (Sandbox Code Playgroud)

将返回身体加上标题,但后来我需要解析它以获取身体.有没有办法以更有用(和更安全)的方式获得两者?

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

ibl*_*lue 445

其中一个解决方案发布在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);
Run Code Online (Sandbox Code Playgroud)

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

  • 这是一个糟糕的解决方案,因为如果您使用代理服务器和代理服务器(例如fiddler)将自己的标头添加到响应中 - 这个标题打破了所有偏移量,您应该使用`list($ header,$ body)= explode("\ r \" n\r \n",$ response,2)`仅作为工作变体 (43认同)
  • 你也可以`list($ header,$ body)= explode("\ r \n\r \n",$ response,2)`,但这可能需要更长的时间,具体取决于你的请求大小. (20认同)
  • @Gremo查看http://www.php.net/manual/en/function.http-parse-headers.php#77241 (8认同)
  • @msangel当响应中有多个标头时,例如服务器执行302重定向时,您的解决方案不起作用.有什么建议? (5认同)
  • @Nate,是的,我知道这一点.AFAIK,但只有一个可能的额外标题 - 代码为"100"(继续).对于此标头,您可以正确定义请求选项:`curl_setopt($ ch,CURLOPT_HTTPHEADER,array('Expect:')); `,禁用发送此标头响应.至于'302`,这不应该发生,因为302头是重定向的,它不期待身体,但是我知道,有时服务器会发送一些带有'302​​`响应的主体,但是到目前为止,浏览器无论如何都会忽略它.为什么curl应该处理这个?) (4认同)
  • `CURLOPT_VERBOSE`用于将进程信息输出到`STDERR`(可能在CLI中打扰),并且讨论的问题是无用的. (4认同)
  • msngel的iblue解决方案的问题已修复在lib_curl 7.30.0 https://github.com/curl/curl/pull/60 - curl现在正确包含CURLINFO_HEADER_SIZE计算中的任何代理头. (3认同)

Geo*_*rey 168

提供此线程的许多其他解决方案都没有正确执行此操作.

  • \r\n\r\nCURLOPT_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;

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

    return $len;
  }
);

$data = curl_exec($ch);
print_r($headers);
Run Code Online (Sandbox Code Playgroud)

  • IMO这是此主题中的最佳答案,并修复了与其他答案一起出现的重定向问题.最好阅读[CURLOPT_HEADERFUNCTION](https://curl.haxx.se/libcurl/c/CURLOPT_HEADERFUNCTION.html)的文档,以了解它的工作原理和潜在的问题.我也对答案进行了一些改进,以帮助其他人. (10认同)
  • @thealexbaron是的,从PHP 5.4开始,请参阅:http://php.net/manual/en/migration54.new-features.php (6认同)
  • 对于这种简洁且符合RFC的方法,此答案被低估了。应该将其设置为粘滞答案并移至顶部。我只希望有一种更快的方法来获取所需标头的值,而不是先解析所有标头。 (3认同)

Ska*_*acc 113

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); 
Run Code Online (Sandbox Code Playgroud)

上面的内容适用于所有内容,不同的协议和代理,您不必担心标题大小,或设置许多不同的curl选项.

PS:要使用对象方法处理标题行,请执行以下操作:

curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$object, 'methodName'))
Run Code Online (Sandbox Code Playgroud)

  • 这是IMO的最佳答案.使用CURLOPT_FOLLOWLOCATION时,它不会导致多个"\ r \n\r \n"出现问题,我猜它不会受到代理的其他标头的影响. (8认同)
  • 是的,这是最好的方法,但是@Geoffrey 的回答通过使用不需要全局变量等的匿名函数使这个更清晰。 (3认同)
  • @MV谢谢,是的,通过"逐行"我的意思是"每个标题".我为了清楚起见编辑了我的答案 要获取整个标题部分(也称为所有标题),您还可以使用对象方法进行回调,以便对象属性可以保存所有标题. (2认同)

use*_*143 40

这是你想要的?

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

  • 这通常正常工作,除非有HTTP/1.1 100继续,然后是中断,然后是HTTP/1.1 200 OK.我会选择另一种方法. (8认同)

小智 10

只需设置选项:

  • CURLOPT_HEADER,0

  • CURLOPT_RETURNTRANSFER,1

并使用curl_getinfo和CURLINFO_HTTP_CODE(或者没有opt param,你将拥有一个包含你想要的所有信息的关联数组)

更多内容:http://php.net/manual/fr/function.curl-getinfo.php

  • 这似乎根本不会向您返回响应标头.或者至少使用`curl_getinfo()`无法检索它们. (4认同)

pr1*_*001 7

如果你特别想要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);
Run Code Online (Sandbox Code Playgroud)

  • @Geoffrey 不,这对于只需要获取 Content-Type 的其他人来说可能很有用 (3认同)
  • @Geoffrey是的,但是所有答案对其他用户也有用,不要忘记它,OP也找到了答案,所以每个人都满意 (3认同)
  • OP 询问是否有一种方法可以检索标题,而不是一个特定的标题,这并没有回答 OP 的问题。 (2认同)