Как выбрать тип содержимого из заголовка HTTP Accept в PHP - PullRequest
18 голосов
/ 26 июня 2009

Я пытаюсь создать стандартную совместимую инфраструктуру веб-сайта, которая использует XHTML 1.1 в качестве application / xhtml + xml или HTML 4.01 в качестве text / html в зависимости от поддержки браузера. В настоящее время он просто ищет «application / xhtml + xml» в любом месте заголовка «accept» и использует его, если он существует, но он не гибкий - text / html может иметь более высокий балл. Кроме того, это станет более сложным, когда будут добавлены другие форматы (WAP, SVG, XForms и т. Д.). Итак, кто-нибудь знает о проверенном и проверенном фрагменте кода PHP для выбора из строкового массива, предоставленного сервером, либо того, который лучше всего поддерживается клиентом, или упорядоченного списка на основе оценки клиента?

Ответы [ 7 ]

20 голосов
/ 06 июля 2009

Маленький фрагмент из моей библиотеки:

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'));
11 голосов
/ 11 июля 2009

Вы можете использовать модуль apache mod_negotiation . Таким образом, вы можете использовать весь спектр возможностей согласования, предлагаемых модулем, включая ваши собственные предпочтения для типа контента (например, «Я действительно хочу доставить application / xhtml + xml, если клиент не очень сильно предпочитает что-то еще "). основное решение:

  • создать файл .htaccess с
    AddHandler type-map .var
    в качестве содержимого
  • создать файл foo.var с
    URI: foo<br>
    URI: foo.php/html
    Content-type: text/html; qs=0.7<br>
    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_negotiation, соответствующие привилегии AllowOverride для AddHandler и AcceptPathInfo , не отключенные для $ _SERVER ['PATH_INFO'].
С моей отправкой Firefox «Принять: текст / html, application / xhtml + xml, application / xml; q = 0,9, / ; q = 0,8» и пример .var map результат «выбранный тип»: XHTML».
Вы можете использовать другие «твики», чтобы избавиться от PATH_INFO или необходимости запрашивать foo .var , но основная концепция такова: пусть mod_negotiation перенаправит запрос на ваш php-скрипт так, чтобы скрипт мог " читать "выбранный тип контента".

Итак, кто-нибудь знает о проверенном и проверенном фрагменте PHP-кода для выбора
Это не чисто php-решение, но я бы сказал, что mod_negotiation было опробовано и протестировано; -)
10 голосов
/ 09 июля 2009

Pear :: HTTP 1.4.1 имеет метод stringgotiateMimeType (массив $ поддерживается, строка $ 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, <em>/</em>;q=0.5 -> <b>application/xhtml+xml</b>
text/*;q=0.3, text/html;q=0.8, application/xhtml+xml;q=0.7, */*;q=0.2 -> <b>text/html</b>
text/*;q=0.3, text/html;q=0.7, */*;q=0.8 -> <b>application/xhtml+xml</b>
text/*, application/xhtml+xml -> <b>application/xhtml+xml</b>
text/html, application/xhtml+xml -> <b>text/html</b>

редактировать: это может быть не так хорошо, в конце концов ...
Мой firefox отправляет Accept: текст / html, application / xhtml + xml, application / xml; q = 0,9, / ; q = 0,8
text / html и application / xhtml + xml имеют q = 1.0, но PEAR :: HTTP (afaik) не позволяет вам выбрать, какой вы предпочитаете, он возвращает text / html независимо от того, что вы передаете как $ поддерживается. Это может или не может быть достаточно для вас. см. мой другой ответ (ы).
9 голосов
/ 27 июля 2013

Для справки: Переговоры - это чистая реализация PHP для работы с переговорами о контенте.

1 голос
/ 19 сентября 2016

Библиотека PEAR HTTP2 поддерживает синтаксический анализ всех типов заголовков Accept. Его можно установить через composer и PEAR.

Примеры можно найти в документации или в моем блоге .

1 голос
/ 28 апреля 2016

Объединены решения @ maciej-Łebkowski и @ chacham15 с моими исправлениями и улучшениями. Если вы передадите $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;
}
0 голосов
/ 05 июня 2017

Клиент может принять список типов пантомимы в ответе. С другой стороны, порядок ответа очень важен для клиентской стороны. PHP Pear HTTP2 лучше всего подходит для работы с языком, кодировкой и миметипами.

$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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...