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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9183178/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 06:19:09  来源:igfitidea点击:

Can PHP cURL retrieve response headers AND body in a single request?

phphttpcurl

提问by gremo

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

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

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?

将返回body 和 headers,但随后我需要解析它以获取正文。有没有办法以更可用(和安全)的方式获得两者?

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

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

回答by iblue

One solution to this was posted in the PHP documentation comments: http://www.php.net/manual/en/function.curl-exec.php#80442

PHP 文档注释中发布了一种解决方案:http: //www.php.net/manual/en/function.curl-exec.php#80442

Code example:

代码示例:

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

Warning:As noted in the comments below, this may not be reliable when used with proxy servers or when handling certain types of redirects. @Geoffrey's answer may handle these more reliably.

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

回答by Geoffrey

Many of the other solutions offered this thread are notdoing this correctly.

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

  • Splitting on \r\n\r\nis not reliable when CURLOPT_FOLLOWLOCATIONis on or when the server responds with a 100 code.
  • Not all servers are standards compliant and transmit just a \nfor new lines.
  • Detecting the size of the headers via CURLINFO_HEADER_SIZEis also not always reliable, especially when proxies are used or in some of the same redirection scenarios.
  • \r\n\r\nCURLOPT_FOLLOWLOCATION打开或服务器以 100 代码响应时,拆分是不可靠的。
  • 并非所有服务器都符合标准并且仅传输\n新线路。
  • 通过检测标头的大小CURLINFO_HEADER_SIZE也并不总是可靠的,尤其是在使用代理或在某些相同的重定向场景中时。

The most correct method is using CURLOPT_HEADERFUNCTION.

最正确的方法是使用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.

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

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!

这符合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);

回答by Skacc

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:

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

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.

以上适用于所有内容,不同的协议和代理,您无需担心标头大小,或设置许多不同的 curl 选项。

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

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

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

回答by user1031143

is this what are you looking to?

这是你要找的吗?

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

回答by Cyril H.

Just set options :

只需设置选项:

  • CURLOPT_HEADER, 0

  • CURLOPT_RETURNTRANSFER, 1

  • CURLOPT_HEADER, 0

  • CURLOPT_RETURNTRANSFER, 1

and use curl_getinfo with CURLINFO_HTTP_CODE (or no opt param and you will have an associative array with all the informations you want)

并将 curl_getinfo 与 CURLINFO_HTTP_CODE 一起使用(或不使用 opt 参数,您将拥有一个包含您想要的所有信息的关联数组)

More at : http://php.net/manual/fr/function.curl-getinfo.php

更多信息:http: //php.net/manual/fr/function.curl-getinfo.php

回答by pr1001

If you specifically want the Content-Type, there's a special cURL option to retrieve it:

如果你特别想要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);

回答by Enyby

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

Works with HTTP/1.1 100 Continuebefore other headers.

工程与HTTP/1.1 100 Continue其他头前。

If you need work with buggy servers which sends only LF instead of CRLF as line breaks you can use preg_splitas follows:

如果您需要使用仅发送 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);

回答by Roy

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.

如果需要,应用 for 循环并删除爆炸限制。

回答by Antony

Here is my contribution to the debate ... This returns a single array with the data separated and the headers listed. This works on the basis that CURL will return a headers chunk [ blank line ] data

这是我对辩论的贡献......这将返回一个单独的数组,其中包含数据和列出的标题。这是基于 CURL 将返回标题块 [空白行] 数据

curl_setopt($ch, CURLOPT_HEADER, 1); // we need this to get headers back
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_VERBOSE, true);

// $output contains the output string
$output = curl_exec($ch);

$lines = explode("\n",$output);

$out = array();
$headers = true;

foreach ($lines as $l){
    $l = trim($l);

    if ($headers && !empty($l)){
        if (strpos($l,'HTTP') !== false){
            $p = explode(' ',$l);
            $out['Headers']['Status'] = trim($p[1]);
        } else {
            $p = explode(':',$l);
            $out['Headers'][$p[0]] = trim($p[1]);
        }
    } elseif (!empty($l)) {
        $out['Data'] = $l;
    }

    if (empty($l)){
        $headers = false;
    }
}

回答by mal

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.

这里有许多答案的问题是,它们"\r\n\r\n"可以合法地出现在 html 的正文中,因此您无法确定是否正确拆分了标题。

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

似乎通过一次调用单独存储标头的唯一方法curl_exec是使用上面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-Lengthheader to substr()as a negative start value.

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