2009-06-26 8 views
17

Estoy intentando construir un marco de sitio web compatible estándar que sirva XHTML 1.1 como application/xhtml + xml o HTML 4.01 como texto/html dependiendo del soporte del navegador. Actualmente solo busca "application/xhtml + xml" en cualquier lugar del encabezado accept, y lo usa si existe, pero eso no es flexible - text/html podría 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 una pieza probada de código PHP para seleccionar, a partir de una matriz de cadenas dada por el servidor, ya sea la mejor soportada por el cliente o una lista ordenada basada en la puntuación del cliente?Cómo seleccionar el tipo de contenido del encabezado HTTP Accept en PHP

+0

Mientras que todo está bien tratar de ser compatible con los estándares y "hacer las cosas bien", creo que es digno de pasar un momento de considerar si realmente va a obtener ningún beneficio de todo esto. P.ej. no es una buena razón para suministrar application/xhtml + xml cuando text/html funciona bien y así sucesivamente. –

Respuesta

11

Puede aprovechar apache's mod_negotiation module. De esta forma puede usar toda la gama de capacidades de negociación que ofrece el módulo, incluyendo sus propias preferencias para el tipo de contenido (e, g, "Realmente deseo entregar la aplicación/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 con 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
    como contenidos
  • crear un archivo con foo.php
    <?php 
    echo 'selected type: ', substr($_SERVER['PATH_INFO'], 1);
    como contenido.
  • solicitud http://localhost/whatever/foo.var

Para que esto funcione necesita mod_negotiation habilitado, los privilegios apropiados para AllowOverride AddHandler y AcceptPathInfo no ser discapacitado por $ _SERVER [ 'PATH_INFO'].
Con mi Firefox enviando "Aceptar: texto/html, application/xhtml + xml, application/xml; q = 0.9, /; q = 0.8" y el ejemplo del mapa .var el resultado es "tipo seleccionado: xhtml" .
Puede utilizar 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 forma que el script pueda "leer" el tipo de contenido seleccionado.

Así que, ¿alguien sabe de una pieza de probada eficacia de código PHP para seleccionar
No es una solución PHP puro, pero yo diría que mod_negotiation ha sido probado ;-)

+0

Aceptado - Incluso mejor que PHP. – l0b0

+3

Prefiero mantener esta lógica dentro de mi código. – Greg

19

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')); 
+0

solo una pequeña mejora: cambie el prototipo de función a 'function getBestSupportedMimeType ($ mimeTypes = null, $ acceptedTypes = FALSE) {if ($ acceptedTypes === FALSE) {$ acceptedTypes = $ _SERVER ['HTTP_ACCEPT']; } ... '. esencialmente permite tipos de aceptación personalizados si el programa necesita hacer algo un poco más personalizado. – chacham15

10

PEAR :: HTTP 1.4.1 tiene un método 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"; 
} 

impresiones

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 Accept: text/html, application/xhtml + xml, application/xml; q = 0,9, /; q = 0,8
text/html y aplicación/xhtml + xml tener q = 1,0 pero PEAR :: HTTP (afaik) no permite que elija cuál prefiere, devuelve texto/html sin importar lo que pase como $ compatible. Esto puede o no ser suficiente para ti. ver mis otras respuestas.

+1

Para el código de PHP 5, use el paquete HTTP2: http://pear.php.net/package/HTTP2 – cweiske

9

Sólo para el registro , Negotiation es una implementación pura de PHP para manejar la negociación de contenido.

1

Merged @ maciej-Łebkowski y @ chacham15 soluciones con mis problemas arreglos y mejoras. Si pasa $desiredTypes = 'text/*' y Accept contiene text/html;q=1, entonces 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://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; 
} 
+1

No debe filtrar q = 0 desde los encabezados Aceptar del cliente. Significa que el cliente no aceptará ese tipo, p. 'Accept-Language: en, en-US; q = 0' significa que aceptaré cualquier inglés siempre que no sea estadounidense. –

0

El cliente puede aceptar una lista de tipos de mimo en la respuesta. Por otro lado, el orden de la respuesta es muy importante para el lado del cliente. PHP Pear HTTP2 es lo mejor para tratar con el lenguaje, el conjunto de caracteres y los tipos miméticos.

$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í es un buen tutorial: https://cweiske.de/tagebuch/php-http-negotiation.htm

Cuestiones relacionadas