【问题标题】:Returning header as array using Curl使用 Curl 将标头作为数组返回
【发布时间】:2012-05-14 19:34:09
【问题描述】:

我正在尝试使用 PHP 从 CURL 获取响应和响应标头,特别是对于 Content-Disposition: attachment;所以我可以返回在标题中传递的文件名。这似乎不会在 curl_getinfo 中返回。

我尝试使用 HeaderFunction 调用函数来读取额外的标题,但是我无法将内容添加到数组中。

请问有人有什么想法吗?


下面是我的代码的一部分,它是一个 Curl 包装类:

 ...
 curl_setopt($this->_ch, CURLOPT_URL, $this->_url);
 curl_setopt($this->_ch, CURLOPT_HEADER, false);
 curl_setopt($this->_ch, CURLOPT_POST, 1);
 curl_setopt($this->_ch, CURLOPT_POSTFIELDS, $this->_postData);
 curl_setopt($this->_ch, CURLOPT_RETURNTRANSFER, 1);
 curl_setopt($this->_ch, CURLOPT_USERAGENT, $this->_userAgent);
 curl_setopt($this->_ch, CURLOPT_HEADERFUNCTION, 'readHeader');

 $this->_response = curl_exec($this->_ch);
 $info = curl_getinfo($this->_ch);
 ...


 function readHeader($ch, $header)
 {
      array_push($this->_headers, $header);
 }

【问题讨论】:

  • 我应该补充一点,readHeader 函数是 curl 包装类的一部分。使用 '$this->readHeader' 不起作用。
  • 根据文档,您的 readHeader 函数必须返回写入的字节数。添加return strlen($header) 应该可以完成这项工作

标签: php curl


【解决方案1】:

在这里,应该这样做:

curl_setopt($this->_ch, CURLOPT_URL, $this->_url);
curl_setopt($this->_ch, CURLOPT_HEADER, 1);
curl_setopt($this->_ch, CURLOPT_RETURNTRANSFER, 1);

$response = curl_exec($this->_ch);
$info = curl_getinfo($this->_ch);

$headers = get_headers_from_curl_response($response);

function get_headers_from_curl_response($response)
{
    $headers = array();

    $header_text = substr($response, 0, strpos($response, "\r\n\r\n"));

    foreach (explode("\r\n", $header_text) as $i => $line)
        if ($i === 0)
            $headers['http_code'] = $line;
        else
        {
            list ($key, $value) = explode(': ', $line);

            $headers[$key] = $value;
        }

    return $headers;
}

【讨论】:

  • 标头名称不区分大小写,因此最好将 $key 小写。此外,多个标题可以具有相同的名称/
  • explode(': ', $line); 标头值可能包含“:”,因此需要第三个参数“2”。 explode(': ', $line, 2); 标题可以多行...
  • 我忘记了:标题可以是多行的,下一个折叠行以空格开头。可能有重复的标题,如“set-cookie” - 当前代码将覆盖以前的代码...
【解决方案2】:

c.hill 的 anwser 很棒,但如果第一个响应是 301 或 302,代码将无法处理 - 在这种情况下,只有第一个标头将添加到 get_header_from_curl_response() 返回的数组中。

我已经更新了函数以返回一个包含每个标题的数组。

首先我使用这行来创建一个只有标题内容的变量

$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($a, 0, $header_size);

然后我将 $header 传递给新的 get_headers_from_curl_response() 函数:

static function get_headers_from_curl_response($headerContent)
{

    $headers = array();

    // Split the string on every "double" new line.
    $arrRequests = explode("\r\n\r\n", $headerContent);

    // Loop of response headers. The "count() -1" is to 
    //avoid an empty row for the extra line break before the body of the response.
    for ($index = 0; $index < count($arrRequests) -1; $index++) {

        foreach (explode("\r\n", $arrRequests[$index]) as $i => $line)
        {
            if ($i === 0)
                $headers[$index]['http_code'] = $line;
            else
            {
                list ($key, $value) = explode(': ', $line);
                $headers[$index][$key] = $value;
            }
        }
    }

    return $headers;
}

此函数将采用如下标题:

HTTP/1.1 302 Found
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/html; charset=utf-8
Expires: -1
Location: http://www.website.com/
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Sun, 08 Sep 2013 10:51:39 GMT
Connection: close
Content-Length: 16313

HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
X-AspNet-Version: 4.0.30319
Date: Sun, 08 Sep 2013 10:51:39 GMT
Connection: close
Content-Length: 15519

并返回一个这样的数组:

(
    [0] => Array
        (
            [http_code] => HTTP/1.1 302 Found
            [Cache-Control] => no-cache
            [Pragma] => no-cache
            [Content-Type] => text/html; charset=utf-8
            [Expires] => -1
            [Location] => http://www.website.com/
            [Server] => Microsoft-IIS/7.5
            [X-AspNet-Version] => 4.0.30319
            [Date] => Sun, 08 Sep 2013 10:51:39 GMT
            [Connection] => close
            [Content-Length] => 16313
        )

    [1] => Array
        (
            [http_code] => HTTP/1.1 200 OK
            [Cache-Control] => private
            [Content-Type] => text/html; charset=utf-8
            [Server] => Microsoft-IIS/7.5
            [X-AspNet-Version] => 4.0.30319
            [Date] => Sun, 08 Sep 2013 10:51:39 GMT
            [Connection] => close
            [Content-Length] => 15519
        )

)

【讨论】:

    【解决方案3】:

    解决问题:

    • 报头内容包含':'(拆分字符串)时出错
    • 不支持多行标题
    • 不支持重复的标头 (Set-Cookie)

    这是我对这个话题的看法;-)

    list($head, $body)=explode("\r\n\r\n", $content, 2);
    $headers=parseHeaders($head); 
    
    function parseHeaders($text) {
        $headers=array();
    
        foreach (explode("\r\n", $text) as $i => $line) {
            // Special HTTP first line
            if (!$i && preg_match('@^HTTP/(?<protocol>[0-9.]+)\s+(?<code>\d+)(?:\s+(?<message>.*))?$@', $line, $match)) {
                $headers['@status']=$line;
                $headers['@code']=$match['code'];
                $headers['@protocol']=$match['protocol'];
                $headers['@message']=$match['message'];
                continue;
            }
    
            // Multiline header - join with previous
            if ($key && preg_match('/^\s/', $line)) {
                $headers[$key].=' '.trim($line);
                continue;
            }
    
            list ($key, $value) = explode(': ', $line, 2);
            $key=strtolower($key);
            // Append duplicate headers - namely Set-Cookie header
            $headers[$key]=isset($headers[$key]) ? $headers[$key].' ' : $value;
        }
    
        return $headers;
    }
    

    【讨论】:

      【解决方案4】:

      使用array() 表单进行方法回调应该可以使原始示例工作:

      curl_setopt($this-&gt;_ch, CURLOPT_HEADERFUNCTION, array($this, 'readHeader'));

      【讨论】:

        【解决方案5】:

        我的另一个实现:

        function getHeaders($response){
        
            if (!preg_match_all('/([A-Za-z\-]{1,})\:(.*)\\r/', $response, $matches) 
                    || !isset($matches[1], $matches[2])){
                return false;
            }
        
            $headers = [];
        
            foreach ($matches[1] as $index => $key){
                $headers[$key] = $matches[2][$index];
            }
        
            return $headers;
        }
        

        用于case,请求格式是:

        主持人:*
        接受:*
        内容长度:*
        等等……

        【讨论】:

          【解决方案6】:

          简单明了

          $headers = [];
          // Get the response body as string
          $response = curl_exec($curl);
          // Get the response headers as string
          $headerSize = curl_getinfo($curl, CURLINFO_HEADER_SIZE);
          // Get the substring of the headers and explode as an array by \r\n
          // Each element of the array will be a string `Header-Key: Header-Value`
          // Retrieve this two parts with a simple regex `/(.*?): (.*)/`
          foreach(explode("\r\n", trim(substr($response, 0, $headerSize))) as $row) {
              if(preg_match('/(.*?): (.*)/', $row, $matches)) {
                  $headers[$matches[1]] = $matches[2];
              }
          }
          

          【讨论】:

            【解决方案7】:

            您可以使用http_parse_headers函数。

            它来自PECL,但你会找到fallbacks in this SO thread

            【讨论】:

            • 说这是“标准”可能有点误导 - 它不是标准扩展,例如不附带 PHP,也没有正式的 Windows 版本。它仍然是0.x,这意味着它不稳定。在它至少被标记为稳定之前,我不会依赖它,最好是正式捆绑为标准 PHP 扩展。
            • @mindplay.dk 你是对的:来自 PECL 并没有使其成为标准。无论如何,pecl_http 自 2006 年 1.0.0 版以来被标记为稳定版,最新的稳定版是 3.1.0 版。我改写了我的答案以使其更清楚。
            【解决方案8】:

            你可以做两种方式

            1. 通过设置 curl_setopt($this->_ch, CURLOPT_HEADER, true); 标头将带有来自 curl_exec() 的响应消息; 您必须从响应消息中搜索关键字“Content-Disposition:”。

            2. 在调用 curl_exec() 后立即使用此函数 get_headers($url)。 $url 是 curl 中调用的 url。返回是标题数组。在数组中搜索“Content-Disposition”以获得您想要的。

            【讨论】:

            • 使用 get_headers() 会不必要地向服务器加载第二个 HTTP 请求,并且很可能会给出一组完全不同的标头。
            【解决方案9】:

            C.hill 的回答很棒,但在检索多个 cookie 时会中断。在这里进行了更改

            public function get_headers_from_curl_response($response) { 
                $headers = array(); 
                $header_text = substr($response, 0, strpos($response, "\r\n\r\n")); 
                foreach (explode("\r\n", $header_text) as $i => $line) 
                     if ($i === 0) $headers['http_code'] = $line; 
                     else { 
                          list ($key, $value) = explode(': ', $line); $headers[$key][] = $value; 
                     } 
                return $headers; 
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-04-07
              • 2021-08-11
              • 2016-12-19
              • 1970-01-01
              • 2016-10-14
              • 2016-07-15
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多