Как выбрать тип контента из заголовка HTTP Accept в PHP
Я пытаюсь создать стандартную структуру веб-сайта, которая поддерживает XHTML 1.1 как application/xhtml + xml или HTML 4.01 как text/html в зависимости от поддержки браузера. В настоящее время он просто ищет "application/xhtml + xml" в любом месте заголовка accept и использует это, если он существует, но это не является гибким - text/html может иметь более высокий балл. Кроме того, он станет более сложным, когда будут добавлены другие форматы (WAP, SVG, XForms и т.д.). Итак, знает ли кто-нибудь о проверенном и проверенном фрагменте кода PHP для выбора из строкового массива, заданного сервером, либо наилучшего, поддерживаемого клиентом, либо упорядоченного списка на основе оценки клиента?
Ответы
Ответ 1
Вы можете использовать модуль mod_negotiation apache. Таким образом, вы можете использовать весь спектр возможностей для переговоров, предлагаемый модулем, включая ваши собственные настройки для типа контента (e, g, "Я действительно хочу доставить приложение /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_negotiation, соответствующие привилегии AllowOverride для AddHandler и AcceptPathInfo не отключены для $_SERVER ['PATH_INFO "].
С моей отправкой Firefox "Accept: text/html, application/xhtml + xml, application/xml; q = 0.9,/; q = 0.8" и пример .var map, результат "selected type: xhtml".
Вы можете использовать другие "твики", чтобы избавиться от PATH_INFO или необходимость запросить foo .var, но основная идея: пусть mod_negotiation перенаправляет запрос на ваш php script таким образом, чтобы script может "читать" выбранный тип содержимого.
Итак, кто-нибудь знает о проверенном и проверенном фрагменте кода PHP для выбора
Это не чистое php-решение, но я бы сказал, что mod_negotiation была проверена и проверена; -)
Ответ 2
Маленький фрагмент из моей библиотеки:
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
Pear:: HTTP 1.4.1 имеет метод строка negotiateMimeType (массив $поддерживается, строка $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
редактирование: это может быть не так хорошо в конце концов...
Мой firefox отправляет Accept: text/html, application/xhtml + xml, application/xml; q = 0.9,/; q = 0.8
text/html и application/xhtml + xml имеют q = 1.0, но PEAR:: HTTP (afaik) не позволяет выбрать you, который вы предпочитаете, он возвращает текст /html независимо от того, что вы передаете как $поддерживается. Это может быть или не быть достаточно для вас. см. мой другой ответ (ы).
Ответ 4
Только для записи Negotiation является чистой реализацией PHP для ведения переговоров по содержимому.
Ответ 5
Объединенные решения @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 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
PEAR HTTP2 library поддерживает разбор всех типов заголовков Accept
. Он устанавливается через композитор и PEAR.
Примеры можно найти в документации или мой пост в блоге.
Ответ 7
http://www.dev-archive.net/articles/xhtml.html#content-negotiation написан на Perl, но он четко изложен и состоит только из некоторых if/else и regex. Портирование его на PHP должно быть тривиальным.
Ответ 8
Клиент может принять список тим-типов в ответе. С другой стороны, порядок ответа очень важен для клиентской стороны. 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