有什么方法可以使用PHP获取cURL请求的标头和正文?我发现这个选项:
curl_setopt($ch, CURLOPT_HEADER, true);
将返回正文和标头,但随后我需要对其进行解析以获取正文。有什么方法可以使两者更加实用(和安全)?
请注意,对于“单个请求”,我的意思是避免在GET / POST之前发出HEAD请求。
有什么方法可以使用PHP获取cURL请求的标头和正文?我发现这个选项:
curl_setopt($ch, CURLOPT_HEADER, true);
将返回正文和标头,但随后我需要对其进行解析以获取正文。有什么方法可以使两者更加实用(和安全)?
请注意,对于“单个请求”,我的意思是避免在GET / POST之前发出HEAD请求。
Answers:
一种解决方案发布在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);
警告:如以下注释中所述,当与代理服务器一起使用或处理某些类型的重定向时,这可能并不可靠。@Geoffrey的答案可能会更可靠地处理这些问题。
list($header, $body) = explode("\r\n\r\n", $response, 2)
,但这可能需要更长的时间,具体取决于您的请求大小。
list($header, $body) = explode("\r\n\r\n", $response, 2)
100
(继续)。对于此标头,您可以正确定义请求选项:curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
,禁用发送此标头响应。至于302
,这不应该发生,因为302标头是重定向的,它不期望正文,但是我知道,有时服务器会发送带有302
响应的正文,但是无论如何它会被浏览器忽略,到目前为止,为什么curl应该处理这个问题? )
CURLOPT_VERBOSE
旨在将过程信息输出到STDERR
(可能会在CLI中打扰),并且对于所讨论的问题没有用。
此线程提供的许多其他解决方案均未正确执行此操作。
\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;
$headers[strtolower(trim($header[0]))][] = trim($header[1]);
return $len;
}
);
$data = curl_exec($ch);
print_r($headers);
$headers = [];
有效的php吗?
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);
上面的方法适用于所有内容,不同的协议和代理,并且您不必担心标题大小,也不必设置很多不同的curl选项。
PS:要使用对象方法处理标题行,请执行以下操作:
curl_setopt($ch, CURLOPT_HEADERFUNCTION, array(&$object, 'methodName'))
这是你在找什么?
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:'));
$response = curl_exec($ch);
list($header, $body) = explode("\r\n\r\n", $response, 2);
A server that does not understand or is unable to comply with any of the expectation values in the Expect field of a request MUST respond with appropriate error status. The server MUST respond with a 417 (Expectation Failed) status if any of the expectations cannot be met or, if there are other problems with the request, some other 4xx status.
100
在“正确”答案下的评论中具有解决方案。
只需设置选项:
CURLOPT_HEADER,0
CURLOPT_RETURNTRANSFER,1
并将curl_getinfo与CURLINFO_HTTP_CODE一起使用(或不使用opt参数,并且您将拥有一个与所需信息关联的数组)
curl_getinfo()
。
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);
$parts = explode("\r\n\r\nHTTP/", $response);
将第三个参数爆炸为2吗?
HTTP/1.1 100 Continue
可以出现很多次
HTTP/1.1 100 Continue
可以出现很多次 他查看案例是否仅出现一次,但在通常情况下是错误的。例如,对于HTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 100 Continue\r\n\r\nHTTP/1.1 200 OK...\r\n\r\n...
他的代码不能正常工作
这是我对辩论的贡献...这将返回一个单独的数组,其中数据分开,并且标头列出。这是基于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;
}
}
这里有很多答案的问题是,它们"\r\n\r\n"
可以合法地出现在html的正文中,因此您不能确定自己正确地拆分了标头。
似乎唯一通过一次调用单独存储标头的方法curl_exec
是使用回调,如上面https://stackoverflow.com/a/25118032/3326494中建议的那样
然后(要可靠地)仅获取请求的正文,您需要将Content-Length
标头的值传递substr()
为负起始值。
list($head, $body) = explode("\r\n\r\n", $response, 2);
,但是如果您使用CURL,则已经为您完成了此操作curl_setopt($ch, CURLOPT_HEADERFUNCTION, $myFunction);
以防万一您不能/不使用CURLOPT_HEADERFUNCTION
或其他解决方案;
$nextCheck = function($body) {
return ($body && strpos($body, 'HTTP/') === 0);
};
[$headers, $body] = explode("\r\n\r\n", $result, 2);
if ($nextCheck($body)) {
do {
[$headers, $body] = explode("\r\n\r\n", $body, 2);
} while ($nextCheck($body));
}
返回带有参考参数的响应头:
<?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;
}
?>
$rtn=explode("\r\n\r\nHTTP/", $rtn, 2);
正确吗?不应该删除爆炸的第三个参数吗?
explode("\r\n\r\n", $parts, 2);
所以两者都是正确的。
如果您真的不需要使用curl;
$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