代码之家  ›  专栏  ›  技术社区  ›  l0b0

如何在PHP中从HTTP接受头中选择内容类型

  •  18
  • l0b0  · 技术社区  · 15 年前

    我正在尝试构建一个标准兼容的网站框架,它将XHTML 1.1作为应用程序/XHTML+XML或HTML 4.01作为文本/HTML,具体取决于浏览器支持。目前,它只在accept头文件中的任何地方查找“application/xhtml+xml”,并在存在的情况下使用它,但这并不灵活——text/html可能有更高的分数。此外,当添加其他格式(WAP、SVG、XForms等)时,它将变得更加复杂。那么,是否有人知道一段经过尝试和测试的PHP代码,可以从服务器提供的字符串数组中选择客户机最支持的代码或基于客户机得分的有序列表?

    8 回复  |  直到 7 年前
        1
  •  11
  •   VolkerK    15 年前

    你可以利用 apache's mod_negotiation module . 通过这种方式,您可以使用模块提供的全部协商功能,包括 您自己的偏好 对于内容类型(例如,“我真的想交付application/xhtml+xml,除非客户机非常喜欢其他东西”)。 碱性溶液:

    • 使用创建.htaccess文件
      AddHandler type-map .var
      作为内容
    • 创建一个文件foo.var
      URI: foo
      URI: foo.php/html Content-type: text/html; qs=0.7
      URI: foo.php/xhtml Content-type: application/xhtml+xml; qs=0.8
      作为内容
    • 使用创建文件foo.php
      <?php
      echo 'selected type: ', substr($_SERVER['PATH_INFO'], 1);
      作为内容。
    • 请求 http://localhost/whatever/foo.var

    要使其工作,需要启用mod_协商,为addhandler和 AcceptPathInfo 未禁用$_server['path_info']。
    我的火狐发送“accept:text/html,application/xhtml+xml,application/xml;q=0.9, / ;q=0.8”和示例.var map结果是“selected type:xhtml”。
    您可以使用其他“调整”来消除路径信息或请求foo的需要 VaR 但基本概念是:让mod_协商以脚本可以“读取”所选内容类型的方式将请求重定向到PHP脚本。

    那么,是否有人知道要选择的经过测试的一段PHP代码
    这不是纯PHP解决方案,但我认为mod_协商已经过尝试和测试;-)
        2
  •  20
  •   Maciej Łebkowski    15 年前

    我的库中的小片段:

    function getBestSupportedMimeType($mimeTypes = null) {
        // Values will be stored in this array
        $AcceptTypes = Array ();
    
        // Accept header is case insensitive, and whitespace isn’t important
        $accept = strtolower(str_replace(' ', '', $_SERVER['HTTP_ACCEPT']));
        // divide it into parts in the place of a ","
        $accept = explode(',', $accept);
        foreach ($accept as $a) {
            // the default quality is 1.
            $q = 1;
            // check if there is a different quality
            if (strpos($a, ';q=')) {
                // divide "mime/type;q=X" into two parts: "mime/type" i "X"
                list($a, $q) = explode(';q=', $a);
            }
            // mime-type $a is accepted with the quality $q
            // WARNING: $q == 0 means, that mime-type isn’t supported!
            $AcceptTypes[$a] = $q;
        }
        arsort($AcceptTypes);
    
        // if no parameter was passed, just return parsed data
        if (!$mimeTypes) return $AcceptTypes;
    
        $mimeTypes = array_map('strtolower', (array)$mimeTypes);
    
        // let’s check our supported types:
        foreach ($AcceptTypes as $mime => $q) {
           if ($q && in_array($mime, $mimeTypes)) return $mime;
        }
        // no mime-type found
        return null;
    }
    

    示例用法:

    $mime = getBestSupportedMimeType(Array ('application/xhtml+xml', 'text/html'));
    
        3
  •  10
  •   VolkerK    15 年前

    Pear::HTTP 1.4.1有一个方法 string negotiateMimeType( array $supported, string $default)

    <?php
    require 'HTTP.php';
    
    foreach(
      array(
        'text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5',
        'text/*;q=0.3, text/html;q=0.8, application/xhtml+xml;q=0.7, */*;q=0.2',
        'text/*;q=0.3, text/html;q=0.7, */*;q=0.8',
        'text/*, application/xhtml+xml',
        'text/html, application/xhtml+xml'
      ) as $testheader) {  
      $_SERVER['HTTP_ACCEPT'] = $testheader;
    
      $http = new HTTP;
      echo $testheader, ' -> ',
        $http->negotiateMimeType( array('application/xhtml+xml', 'text/html'), 'application/xhtml+xml'),
        "\n";
    }
    

    印刷品

    text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, /;q=0.5 -> application/xhtml+xml
    text/*;q=0.3, text/html;q=0.8, application/xhtml+xml;q=0.7, */*;q=0.2 -> text/html
    text/*;q=0.3, text/html;q=0.7, */*;q=0.8 -> application/xhtml+xml
    text/*, application/xhtml+xml -> application/xhtml+xml
    text/html, application/xhtml+xml -> text/html

    编辑:这可能不太好…
    我的火狐发送 接受:text/html,application/xhtml+xml,application/xml;q=0.9, / q=0.8
    text/html和application/xhtml+xml的q=1.0,但是pear::http(afaik)不允许 选择您喜欢的一个,它返回文本/html,无论您作为$SUPPORTED传递什么。这对您来说可能足够,也可能不够。看看我的其他答案。
        4
  •  9
  •   William Durand    11 年前

    只是为了记录, Negotiation 是处理内容协商的纯PHP实现。

        5
  •  1
  •   Sergey Nevmerzhitsky    9 年前

    将@maciej-ebkowski和@chachacham15解决方案与我的问题修复和改进合并。如果你通过 $desiredTypes = 'text/*' Accept 包含 text/html;q=1 然后 text/html 将被退回。

    /**
     * Parse, sort and select best Content-type, supported by a user browser.
     *
     * @param string|string[] $desiredTypes The filter of desired types. If &null then the all supported types will returned.
     * @param string $acceptRules Supported types in the HTTP Accept header format. $_SERVER['HTTP_ACCEPT'] by default.
     * @return string|string[]|null Matched by $desiredTypes type or all accepted types.
     * @link Inspired by http://stackoverflow.com/a/1087498/3155344
     */
    function resolveContentNegotiation($desiredTypes = null, $acceptRules = null)
    {
        if (!$acceptRules) {
            $acceptRules = @$_SERVER['HTTP_ACCEPT'];
        }
        // Accept header is case insensitive, and whitespace isn't important.
        $acceptRules = strtolower(str_replace(' ', '', $acceptRules));
    
        $sortedAcceptTypes = array();
        foreach (explode(',', $acceptRules) as $acceptRule) {
            $q = 1; // the default accept quality (rating).
            // Check if there is a different quality.
            if (strpos($acceptRule, ';q=') !== false) {
                // Divide "type;q=X" into two parts: "type" and "X"
                list($acceptRule, $q) = explode(';q=', $acceptRule, 2);
            }
            $sortedAcceptTypes[$acceptRule] = $q;
        }
        // WARNING: zero quality is means, that type isn't supported! Thus remove them.
        $sortedAcceptTypes = array_filter($sortedAcceptTypes);
        arsort($sortedAcceptTypes, SORT_NUMERIC);
    
        // If no parameter was passed, just return parsed data.
        if (!$desiredTypes) {
            return $sortedAcceptTypes;
        }
    
        $desiredTypes = array_map('strtolower', (array) $desiredTypes);
    
        // Let's check our supported types.
        foreach (array_keys($sortedAcceptTypes) as $type) {
            foreach ($desiredTypes as $desired) {
                if (fnmatch($desired, $type)) {
                    return $type;
                }
            }
        }
    
        // No matched type.
        return null;
    }
    
        6
  •  1
  •   cweiske agentofuser    8 年前

    PEAR's HTTP2 library 支持分析所有类型的 Accept 标题。它可以通过安装 composer 还有梨。

    示例见 documentation my blog post .

        7
  •  0
  •   Quentin    15 年前

    http://www.dev-archive.net/articles/xhtml.html#content-negotiation 它是用Perl编写的,但它的布局很清楚,只包含一些if/else和regex。将它移植到PHP应该很简单。

        8
  •  0
  •   Mostafa Barmshory    7 年前

    客户机可以接受响应中的mime类型列表。另一方面,响应的顺序对于客户端非常重要。 PHP Pear HTTP2 最好是处理语言、字符集和mimetype。

    $http = new HTTP2();
    $supportedTypes = array(
        'text/html',
        'application/json'
    );
    
    $type = $http->negotiateMimeType($supportedTypes, false);
    if ($type === false) {
        header('HTTP/1.1 406 Not Acceptable');
        echo "You don't want any of the content types I have to offer\n";
    } else {
        echo 'I\'d give you data of type: ' . $type . "\n";
    }
    

    以下是一个很好的教程: https://cweiske.de/tagebuch/php-http-negotiation.htm