2009-06-26 15 views
17

Próbuję zbudować standardową, zgodną ze standardem witrynę internetową, która obsługuje XHTML 1.1 jako application/xhtml + xml lub HTML 4.01 jako text/html w zależności od obsługi przeglądarki. Obecnie po prostu szuka "application/xhtml + xml" w dowolnym miejscu w nagłówku accept i używa go, jeśli istnieje, ale to nie jest elastyczne - tekst/html może mieć wyższy wynik. Ponadto stanie się bardziej skomplikowany, gdy dodane zostaną inne formaty (WAP, SVG, XForms itp.). Czy ktoś wie o wypróbowanym i przetestowanym fragmencie kodu PHP do wyboru, z tablicy łańcuchów podanej przez serwer, albo najlepiej obsługiwanej przez klienta, albo uporządkowanej listy na podstawie wyniku klienta?Jak wybrać typ zawartości z nagłówka HTTP Accept w PHP

+0

Chociaż to wszystko jest dobre, aby spróbować i być zgodny ze standardami i „robić rzeczy właściwe”, myślę, że warto poświęcić chwilę zastanowić się, czy będzie rzeczywiście uzyskać żadnych korzyści z to wszystko. Na przykład. nie ma wiele powodów, by dostarczyć aplikację/xhtml + xml, gdy tekst/HTML działa dobrze i tak dalej. –

Odpowiedz

11

Możesz wykorzystać apache's mod_negotiation module. W ten sposób możesz wykorzystać pełny zakres możliwości negocjacyjnych oferowanych przez moduł, w tym dla twoich preferencji dla typu zawartości (e, g, "Naprawdę chcę dostarczyć aplikację/xhtml + xml, chyba że klient bardzo woli coś innego "). podstawowe rozwiązanie:

  • utworzyć plik .htaccess z
    AddHandler type-map .var
    jako zawartość
  • stworzyć foo.var plików z
    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
    jako zawartość
  • stworzyć foo.php plików z
    <?php 
    echo 'selected type: ', substr($_SERVER['PATH_INFO'], 1);
    jako zawartość.
  • prośba http://localhost/whatever/foo.var

Aby to zadziałało trzeba mod_negotiation włączone odpowiednie przywileje AllowOverride dla AddHandler i AcceptPathInfo nie zostanie wyłączona za $ _SERVER [ 'PATH_INFO'].
Z moim Firefox wysyłanie "Zaakceptuj: tekst/html, aplikacja/xhtml + xml, aplikacja/xml; q = 0.9, /; q = 0,8" i przykładowa mapa .var wynik jest "wybrany typ: xhtml" .
Możesz użyć innych "poprawek", aby pozbyć się PATH_INFO lub potrzeby żądania foo .var, ale podstawową koncepcją jest: niech mod_negotiation przekieruje żądanie do skryptu php w sposób, który skrypt może "odczytać" wybrany typ zawartości.

Więc, czy ktoś wie o sprawdzonej kawałek kodu PHP aby wybrać
To nie jest czysty roztwór php ale powiedziałbym mod_negotiation został sprawdzony ;-)

+0

Zaakceptowany - Nawet lepszy niż PHP. – l0b0

+3

Wolę zachować tę logikę w moim kodzie. – Greg

19

mały fragment z mojej biblioteki:

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; 
} 

Przykład użycia:

$mime = getBestSupportedMimeType(Array ('application/xhtml+xml', 'text/html')); 
+0

tylko odrobinę ulepszenie: zmień prototyp funkcji na 'function getBestSupportedMimeType ($ mimeTypes = null, $ acceptedTypes = FALSE) {if ($ acceptedTypes === FALSE) {$ acceptedTypes = $ _SERVER ['HTTP_ACCEPT']; } ... '. zasadniczo pozwalają niestandardowe typy akceptacji, jeśli program musi zrobić coś bardziej niestandardowego. – chacham15

10

Pear :: HTTP 1.4.1 ma metodę 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"; 
} 

nadrukami

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

edycji : w końcu może nie być tak dobrze ...
My zapalić wysyła Accept: text/html, application/xhtml + xml, application/xml, Q = 0,9, / q = 0,8
text/html i application/xhtml + xml ma q = 1.0, ale PEAR :: HTTP (afaik) nie pozwala, aby wybrałeś, który wolisz, zwraca tekst/html bez względu na to, co przekazujesz jako $ support. To może, ale nie musi, być dla ciebie wystarczające. zobacz moją drugą odpowiedź (odpowiedzi).

+1

Dla kodu PHP 5 użyj pakietu HTTP2: http://pear.php.net/package/HTTP2 – cweiske

9

Tylko dla rekordu , Negotiation to czysta implementacja PHP do obsługi negocjacji treści.

1

Scalono rozwiązania @ maciej-Łebkowski i @ chacham15 z rozwiązaniami problemów i ulepszeniami. Jeśli podasz $desiredTypes = 'text/*' i Accept zawiera text/html;q=1, zostanie zwrócony 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

Nie można odfiltrować q = 0 z nagłówków Accept klienta. Oznacza to, że klient nie zaakceptuje tego typu, np. 'Accept-Language: en, en-US; q = 0' oznacza, że ​​zaakceptuję dowolny angielski, o ile nie jest amerykański. –

0

Klient może zaakceptować listę typów mime w odpowiedzi. Z drugiej strony kolejność odpowiedzi jest bardzo ważna dla strony klienta. PHP Pear HTTP2 to najlepiej radzić sobie z językiem, charset i mimetypes.

$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"; 
} 

Oto dobry poradnik: https://cweiske.de/tagebuch/php-http-negotiation.htm

Powiązane problemy