【问题标题】:How to get the functionality of http_parse_headers without PECL?如何在没有 PECL 的情况下获得 http_parse_headers 的功能?
【发布时间】:2011-09-16 03:35:35
【问题描述】:

我在 Windows 上使用 PHP 5.3.5,但我没有找到任何适用于我的安装的 pecl_http.dll

所以我的问题是,

如何在不使用 PECL 的情况下获得 http_parse_headers 的功能?

【问题讨论】:

标签: php http


【解决方案1】:
function parse_headers($headers)
{
    $headers = preg_replace('/^\r\n/m', '', $headers);
    $headers = preg_replace('/\r\n\s+/m', ' ', $headers);
    preg_match_all('/^([^: ]+):\s(.+?(?:\r\n\s(?:.+?))*)?\r\n/m', $headers . "\r\n", $matches);

    $result = array();
    foreach ($matches[1] as $key => $value)
        $result[$value] = (array_key_exists($value, $result) ? $result[$value] . "\n" : '') . $matches[2][$key];

    return $result;
}

【讨论】:

    【解决方案2】:

    由于我曾经找不到任何与 PECL 完全一样的函数,所以我自己编写了自己的函数,与其他函数相比,它变得相当快……

    function dirtyHeaderParser($headers, $strict = true){
        $arr = array();
        $s = strtok($headers, ':');
        while ($s){
            if ( ($s[0] === ' ') || ($s[0] === "\t") ){
                if (count($arr) != 0){
                    $tail = strtok('');
                    $tail = "{$s}:{$tail}";
                    $v = strtok($tail, "\n");
                    if (is_array($arr[$key])){
                        end($arr[$key]);
                        $last = key($arr[$key]);
                        $arr[$key][$last] = "{$arr[$key][$last]}\n{$v}";
                        reset($arr[$key]);
                    } else {
                        $arr[$key] = "{$arr[$key]}\n{$v}";
                    }
                }
            } else {
                $v = strtok("\n");
                if ($v){
                    $key = strtolower($s);
                    if (((strpos($key, "\n") !== false) || (strpos($key, "\t") !== false) || (strpos($key, " ") !== false)) && $strict) {
                        return false;
                    } 
                    if (array_key_exists($key, $arr)){
                        if (!is_array($arr[$key])){
                            $arr[$key] = array($arr[$key]);
                        }
                        $arr[$key][] = trim($v);
                    } else {
                        $arr[$key] = trim($v);
                    }
                } else {
                    break;
                }
            }
            $s = strtok(':');
        }
        return (count($arr) == 0) ? false : $arr;
    }
    

    严格模式意味着,如果标题键包含\n、空格或\t,它将返回false。 它支持多行标题和双标题值(也支持多行),如果有人发现某些行为不像 PECL 版本,如果您发表评论,我会很高兴。

    【讨论】:

      【解决方案3】:

      讨厌继续这么长的旧线程,但这个更好,因为:

      • 不使用正则表达式
      • 正确处理重复的标题
      • 没有那么多数组和字符串突变调用
      • 解析状态行

      (您可能需要代码来检查扩展是否已经存在)

      function http_parse_headers ($raw) {
          $res = [];
          foreach (explode("\n", $raw) as $h) {
              $h = explode(':', $h, 2);
              $first = trim($h[0]);
              $last = trim($h[1]);
              if (array_key_exists($first, $res)) {
                  $res[$first] .= ", " . $last;
              } else if (isset($h[1])) {
                  $res[$first] = $last;
              } else {
                  $res[] = $first;
              }
          }
          return $res;
      }
      

      【讨论】:

      • 我意识到这是一个旧答案,但对于遇到它的任何人(就像我刚才所做的那样),请注意:任何不完全模仿原始 pecl 功能(包括错误和疏忽)不是“优越的”。
      【解决方案4】:

      很遗憾,对于文档页面提供的功能,如果有两个以上相同的header(即Set-Cookie),则数组的结构会不正确,例如:

          [Set-Cookie] => Array
          (
              [0] => Array
              (
                  [0] => AWESOMESESSIONID=xQ5TRl1GXDQcQCXytfb1PK!-744872953!NONE; Path=/cte-lps2s-test2/; Secure
                  [1] => AWESOME_SESSH_c25c28d0-b763-11df-979f23a029aa77=%2Fcte-lps2s-test2; Path=/
              ) 
              [1] => MOREAWESOME=2_uGgNpo2-jm-CjfaefLzjFhmmP-y3HzwNZKE0gsTeP+; Path=/; Secure
          )
      

      下面对代码的修改将修复这个错误(见评论):

      if( !function_exists( 'http_parse_headers' ) ) {
           function http_parse_headers( $header )
           {
               $retVal = array();
               $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ',$header));
               foreach( $fields as $field ) {
                   if( preg_match('/([^:]+): (.+)/m', $field, $match) ) {
                       $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")',     strtolower(trim($match[1])));
                       // We need to check if an array of values has already been created, to     maintain the correct level in the $retVal array.
                       if(is_array($retVal[$match[1]])) {
                              $retVal[$match[1]] []= $match[2];
                       }
                       else {
                          $retVal[$match[1]] = array($retVal[$match[1]], $match[2]);
                       }
                   } else {
                       $retVal[$match[1]] = trim($match[2]);
                   }
               }
               return $retVal;
           }
      }
      

      【讨论】:

      • 这使用了已弃用的 /e 修饰符。
      • @markus 你是一个在stackoverflow上对所有弃用代码示例进行cmet的机器人吗?你真的有你的工作为你完成。所以像文档说的那样用 preg_replace_callback() 替换上面的内容。事情发生了变化。
      • 为什么我需要成为机器人来指出不推荐使用的代码示例?这是重要的信息,应该进入答案或提示答案的作者改编或删除。
      【解决方案5】:

      这也是从PHP Documentation for http_parse_headers 中提取的。当我将它与@Berry Langerak 的答案(使用microtime)进行比较时,我发现它平均快了 17%,使用 10 个标头的样本(可能是因为它不使用正则表达式)。

      if (!function_exists('http_parse_headers')) {
          function http_parse_headers($raw_headers) {
              $headers = array();
              $key = '';
      
              foreach(explode("\n", $raw_headers) as $i => $h) {
                  $h = explode(':', $h, 2);
      
                  if (isset($h[1])) {
                      if (!isset($headers[$h[0]]))
                          $headers[$h[0]] = trim($h[1]);
                      elseif (is_array($headers[$h[0]])) {
                          $headers[$h[0]] = array_merge($headers[$h[0]], array(trim($h[1])));
                      }
                      else {
                          $headers[$h[0]] = array_merge(array($headers[$h[0]]), array(trim($h[1])));
                      }
      
                      $key = $h[0];
                  }
                  else { 
                      if (substr($h[0], 0, 1) == "\t")
                          $headers[$key] .= "\r\n\t".trim($h[0]);
                      elseif (!$key) 
                          $headers[0] = trim($h[0]); 
                  }
              }
              
              return $headers;
          }
      }
      

      注意:这包括作者在稍后评论中指出的small mistake 的修复。


      正则表达式函数

      0.00035881996
      0.00036096572
      0.00034999847
      0.00043797492
      0.00033497810

      平均:0.000368547434

      这个功能

      0.00006198883
      0.00006604194
      0.00007104873
      0.00006413459
      0.00006389617

      平均 0.000065422052

      【讨论】:

      • 而且最重要的是它确实使用了已弃用的 /e 修饰符,该修饰符在最新的 php 版本中不适用。谢谢!
      【解决方案6】:

      这是来自文档页面的修改版本,其工作方式与 PECL 版本类似 :)

      function http_parse_headers( $header ) {
              $retVal = array();
              $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header));
              foreach( $fields as $field ) {
                  if( preg_match('/([^:]+): (.+)/m', $field, $match) ) {
                      $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1])));
                      if( isset($retVal[$match[1]]) ) {
                          if ( is_array( $retVal[$match[1]] ) ) {
                              $i = count($retVal[$match[1]]);
                              $retVal[$match[1]][$i] = $match[2];
                          }
                          else {
                              $retVal[$match[1]] = array($retVal[$match[1]], $match[2]);
                          }
                      } else {
                          $retVal[$match[1]] = trim($match[2]);
                      }
                  }
              }
              return $retVal;
          }
      

      【讨论】:

      • 这使用了已弃用的 /e 修饰符。
      【解决方案7】:

      您可以在以下位置获取适用于 Windows 的扩展程序

      它是php_http-5.3-*-x86.zip 文件之一。检查您安装了哪个 PHP 并选择正确的,例如我的 PHP 是 php-5.3.6-nts-Win32-VC9-x86,所以我需要 php_http-5.3-nts-svn20091125-vc9-x86.zip。

      下载 zip 并将 php_http.dll 解压缩到您的扩展文件夹。扩展文件夹应该是 php 安装目录中的 /ext 文件夹。如果不确定,请打开 php.ini 文件并搜索以下行:

      ; Directory in which the loadable extensions (modules) reside.
      ; http://php.net/extension-dir
      ; extension_dir = "./"
      ; On windows:
      extension_dir = .\ext
      

      extension_dir 的值是您必须放置 dll 的位置。如果您不确定 php.ini 的位置,请打开命令提示符并执行

      php --ini
      

      这会告诉你你的 php.ini 在哪里。它会输出类似

      Configuration File (php.ini) Path: C:\Windows
      Loaded Configuration File:         C:\php5\php-5.3.6-nts-Win32-VC9-x86\php.ini
      Scan for additional .ini files in: (none)
      Additional .ini files parsed:      (none)
      

      复制 dll 后,将扩展名添加到 php.ini 以启用它。找到它说类似的地方

      ;;;;;;;;;;;;;;;;;;;;;;
      ; Dynamic Extensions ;
      ;;;;;;;;;;;;;;;;;;;;;;
      

      应该有多行类似这样:

      ;extension=php_sybase_ct.dll
      extension=php_tidy.dll
      ;extension=php_xmlrpc.dll
      extension=php_xsl.dll
      ;extension=php_zip.dll
      

      添加以下行:

      extension=php_http.dll
      

      保存 php.ini 并在命令提示符下键入以下内容:

      php --ri http
      

      你现在应该得到一个相当广泛的输出,以

      开头
      http
      HTTP Support => enabled
      Extension Version => 1.7.0-dev
      … more stuff
      

      这意味着,您已经成功安装了扩展程序,现在可以使用它了。

      注意,为了能够在 Windows 上加载此扩展,您还需要加载以下 PHP 扩展:hash、iconv 和 SPL。

      【讨论】:

      • (1)。 Hash,iconv,SPL -> 我不知道它们是什么,我什么也没做。 (2)。它有效:) 我不知道那个链接。只需下载所需的 dll 并执行扩展部分。谢谢
      • @Shrinath Hash、Iconv 和 SPL 是附加扩展。 IIRC 它们在 PHP5.3 中默认启用,这就是为什么你不必对它们做任何事情的原因。我只是觉得我应该注意它的完整性。
      • 我喜欢“注意完整性”部分 :) 谢谢 :)
      • 我需要你的帮助,在我安装了 dll 之后,apache 一直告诉我:你的计算机中缺少 php5.dll,请回复
      • 技术上没有回答这个问题,“我如何......不使用 PECL?”。但这里可能是“正确”的答案。
      【解决方案8】:

      从文档页面,第一条评论:

       if( !function_exists( 'http_parse_headers' ) ) {
           function http_parse_headers( $header )
           {
               $retVal = array();
               $fields = explode("\r\n", preg_replace('/\x0D\x0A[\x09\x20]+/', ' ', $header));
               foreach( $fields as $field ) {
                   if( preg_match('/([^:]+): (.+)/m', $field, $match) ) {
                       $match[1] = preg_replace('/(?<=^|[\x09\x20\x2D])./e', 'strtoupper("\0")', strtolower(trim($match[1])));
                       if( isset($retVal[$match[1]]) ) {
                           $retVal[$match[1]] = array($retVal[$match[1]], $match[2]);
                       } else {
                           $retVal[$match[1]] = trim($match[2]);
                       }
                   }
               }
               return $retVal;
           }
      }
      

      或者,您可能想了解如何在 Windows 上安装 PECL 扩展,老实说,我对此一无所知。

      【讨论】:

      • 保持线程打开只是为了看看那里是否有任何替代建议......
      • 哦,等等,我也看到了:“它可能不像 PECL 版本那么健壮,因为它仅在标题由换行符 (\n) 分隔时才解析。”在同一页!! :(
      • @Shrinath 这是另一个例子,所以不适用于这个函数。你真的尝试过使用它吗?它认为它实际上与 http_parse_headers 的工作方式相同,尽管可能有点不同。
      • @Berry Langerak : 对不起老兄,@Gordon 的人把它拿来了那个链接:) 这实际上解决了我的目的,即使那不是实际的问题。
      • @Shrinath 不用担心。 :) 如果这对你有帮助,那就对你有帮助!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-11-27
      • 2023-01-24
      • 1970-01-01
      • 1970-01-01
      • 2017-10-29
      相关资源
      最近更新 更多