type - Cómo seleccionar el tipo de contenido del encabezado HTTP Accept en PHP
php print headers received (8)
Estoy tratando de construir un marco de sitio web compatible con XHTML 1.1 como application / xhtml + xml o HTML 4.01 como texto / html, según la compatibilidad del navegador. Actualmente solo busca "application / xhtml + xml" en cualquier parte del encabezado de aceptación, y usa eso si existe, pero eso no es flexible, el texto / html puede tener una puntuación más alta. Además, se volverá más complejo cuando se agreguen otros formatos (WAP, SVG, XForms, etc.). Entonces, ¿alguien sabe de un trozo probado de código PHP para seleccionar, de una matriz de cadenas proporcionada por el servidor, ya sea la que mejor admite el cliente o una lista ordenada basada en la puntuación del cliente?
El cliente puede aceptar una lista de tipos mime en la respuesta. Por otro lado, el orden de la respuesta es muy importante para el lado del cliente. PHP Pear HTTP2 es el mejor para tratar con el lenguaje, el conjunto de caracteres y los tipos MIME.
$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";
}
Aquí hay un buen tutorial: https://cweiske.de/tagebuch/php-http-negotiation.htm
Pear :: HTTP 1.4.1 tiene una cadena de método negocieMimeType (array $ compatible, cadena $ por defecto)
<?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";
}
huellas dactilares
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
Edición: esto podría no ser tan bueno después de todo ...
Mi Firefox envía Aceptar: texto / html, application / xhtml + xml, application / xml; q = 0.9, / ; q = 0.8
text / html y application / xhtml + xml tienen q = 1.0, pero PEAR :: HTTP (afaik) no le permite elegir cuál prefiere, devuelve texto / html sin importar lo que pase como $ support. Esto puede o no puede ser suficiente para usted. ver mi otra respuesta (s).
Puede aprovechar el mod_negotiation modulo de apache . De esta manera, puede utilizar la gama completa de capacidades de negociación que ofrece el módulo, incluidas sus propias preferencias para el tipo de contenido (e, g, "Tengo muchas ganas de entregar application / xhtml + xml, a menos que el cliente prefiera algo más") . solución básica:
- crear un archivo .htaccess con
AddHandler type-map .var como contenidos
- crear un archivo foo.var con
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 como contenidos - crear un archivo foo.php con
<?php echo ''selected type: '', substr($_SERVER[''PATH_INFO''], 1); como contenidos.
- solicite http://localhost/whatever/foo.var
Para que esto funcione, necesita habilitar mod_negociación, los privilegios de AllowOverride apropiados para AddHandler y AcceptPathInfo no se deshabilitan para $ _SERVER [''PATH_INFO''].
Con mi Firefox enviando "Aceptar: texto / html, application / xhtml + xml, application / xml; q = 0.9, / ; q = 0.8" y el ejemplo. Mapear el resultado es "tipo seleccionado: xhtml".
Puede usar otros "ajustes" para deshacerse de PATH_INFO o la necesidad de solicitar foo .var , pero el concepto básico es: dejar que mod_negotiation redirija la solicitud a su script php de manera que el script pueda "leer" el contenido seleccionado. tipo.
Entonces, ¿alguien sabe de una pieza probada de código PHP para seleccionarNo es una solución php pura, pero diría que se ha probado y probado mod_negotiation ;-)
Se fusionaron las soluciones @ maciej-Łebkowski y @ chacham15 con mis problemas solucionados y mejoras. Si pasa $desiredTypes = ''text/*''
y Accept
contiene text/html;q=1
, se devolverá 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://.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;
}
Solo para el registro, la Negotiation es una implementación PHP pura para tratar con la negociación de contenido.
Un pequeño fragmento de mi biblioteca:
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;
}
ejemplo de uso:
$mime = getBestSupportedMimeType(Array (''application/xhtml+xml'', ''text/html''));
pear.php.net/package/HTTP2 admite el análisis de todos los tipos de encabezados de Accept
. Es instalable vía composer y PEAR.
Se pueden encontrar ejemplos en la documentation o en la publicación de mi blog .
http://www.dev-archive.net/articles/xhtml.html#content-negotiation está escrito en Perl, pero está claramente establecido y solo consta de algunos if / else y regex. Portar a PHP debería ser trivial.