Can PHP cURL retrieve response headers AND body in a single request?
是否有任何方法可以使用PHP同时获取curl请求的头和体?我发现这个选项:
1 |
将返回body加上headers,但是我需要解析它来获取body。有没有什么方法可以更有效(更安全)地实现这两个目标?
注意,对于"单一请求",我的意思是避免在GET/POST之前发出HEAD请求。
其中一个解决方案发布在php文档注释中:http://www.php.net/manual/en/function.curl exec.php_80442
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 | $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); |
警告:如下面的注释所述,当与代理服务器一起使用或处理某些类型的重定向时,这可能不可靠。@杰弗里的回答可能会更可靠地处理这些问题。
此线程提供的许多其他解决方案没有正确执行此操作。
- 当
CURLOPT_FOLLOWLOCATION 打开或服务器用100代码响应时,在 - 并不是所有的服务器都符合标准,只传输一个用于新线路的
。
- 通过
CURLINFO_HEADER_SIZE 检测头的大小也不总是可靠的,特别是在使用代理或在某些相同的重定向场景中。
最正确的方法是使用
下面是一个非常干净的方法,可以使用PHP闭包来执行此操作。它还将所有头转换为小写,以便跨服务器和HTTP版本进行一致的处理。
此版本将保留重复的邮件头
这符合rfc822和rfc2616,请不要建议编辑以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | $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; $name = strtolower(trim($header[0])); if (!array_key_exists($name, $headers)) $headers[$name] = [trim($header[1])]; else $headers[$name][] = trim($header[1]); return $len; } ); $data = curl_exec($ch); print_r($headers); |
号
curl有一个内置选项,称为curlot_headerfunction。此选项的值必须是回调函数的名称。curl将通过收割台(仅限于收割台!)对于这个回调函数,一行一行地调用(这样函数将从头部分的顶部开始为每个头行调用)。然后回调函数可以对其执行任何操作(并且必须返回给定行的字节数)。下面是一个经过测试的工作代码:
1 2 3 4 5 6 7 8 9 10 11 | function HandleHeaderLine( $curl, $header_line ) { echo"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选项。
P.S.:要使用对象方法处理标题行,请执行以下操作:
1 |
这就是你想要的吗?
1 2 3 4 5 6 7 | curl_setopt($ch, CURLOPT_HTTPHEADER, array('Expect:')); $response = curl_exec($ch); list($header, $body) = explode(" ", $response, 2); |
号
设置选项:
Curlopt_标题,0
卷取返回传输,1
使用curlinfo和curlinfo代码(或者不使用opt-param,您将拥有一个包含所有所需信息的关联数组)
更多信息,请访问:http://php.net/manual/fr/function.curl-getinfo.php
如果您特别想要
1 2 3 4 | $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $response = curl_exec($ch); $content_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_VERBOSE, 1); curl_setopt($ch, CURLOPT_HEADER, 1); $parts = explode(" HTTP/", $response); $parts = (count($parts) > 1 ? 'HTTP/' : '').array_pop($parts); list($headers, $body) = explode(" ", $parts, 2); |
在其他头文件之前使用
如果您需要使用只发送LF而不是CRLF作为换行符的buggy服务器,可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_VERBOSE, 1); curl_setopt($ch, CURLOPT_HEADER, 1); $parts = preg_split("@ ? ? HTTP/@u", $response); $parts = (count($parts) > 1 ? 'HTTP/' : '').array_pop($parts); list($headers, $body) = preg_split("@ ? ? @u", $parts, 2); |
。
这是我对辩论的贡献…这将返回一个单独的数组,其中数据被分隔,头被列出。这是基于curl将返回头块[空行]数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | 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(" ",$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; } } |
号
我的方法是
1 2 3 4 5 6 7 8 9 10 11 12 13 | $response = curl_exec($ch); $x = explode(" ", $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循环并删除爆炸限制。
这里有许多答案的问题是,
"可以合法地出现在HTML的主体中,因此您不能确定是否正确地拆分了头。
似乎只有一个调用
然后为了(可靠地)得到请求的主体,您需要将
当您需要从服务器返回的最后一个内容时,请小心。此代码可能会在等待实际(最后)头和正文时打破您的期望:
", $result, 2);。
下面是获取最终头部和身体部位的简单方法;
1 2 3 4 5 6 7 8 9 10 11 12 13 | $result = explode(" ", $result); // drop redirect etc. headers while (count($result) > 2) { array_shift($result); } // split headers / body parts @ list($headers, $body) = $result; |
号
返回带有引用参数的响应头:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | <?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(" HTTP/", $rtn, 2); //to deal with"HTTP/1.1 100 Continue HTTP/1.1 200 OK... ..." header $rtn=(count($rtn)>1 ? 'HTTP/' : '').array_pop($rtn); list($str_resp_headers, $rtn)=explode(" ", $rtn, 2); $str_resp_headers=explode(" ", $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; } ?> |
。
如果你真的不需要使用卷发;
1 2 3 | $body = file_get_contents('http://example.com'); var_export($http_response_header); var_export($body); |
哪些输出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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> Example Domain... |
。
请参阅http://php.net/manual/en/reserved.variables.httpResponseHeader.php