2009-06-26 3 views
17

Ich versuche, ein standardkonformes Website-Framework zu erstellen, das XHTML 1.1 als application/xhtml + xml oder HTML 4.01 als text/html je nach Browserunterstützung anbietet. Momentan sucht es einfach nach "application/xhtml + xml" irgendwo im accept-Header und benutzt das, wenn es existiert, aber das ist nicht flexibel - text/html könnte eine höhere Punktzahl haben. Außerdem wird es komplexer, wenn andere Formate (WAP, SVG, XForms usw.) hinzugefügt werden. Kennt also jemand ein erprobtes Stück PHP-Code, um aus einem String-Array, das vom Server bereitgestellt wird, entweder das vom Client am besten unterstützte oder eine geordnete Liste basierend auf dem Client-Score auszuwählen?So wählen Sie den Inhaltstyp aus HTTP Akzeptieren Header in PHP

+0

Während es alles gut zu versuchen und sein Standards kompatibel und ist „Dinge richtig tun“, ich denke, es lohnt sich in Anbetracht einen Moment Zeit zu verbringen, wenn Sie irgendwelche Vorteile gewinnen tatsächlich aus all das. Z.B. nicht gerade ein Grund, um application/xhtml + xml zu liefern, wenn text/html gut funktioniert und so weiter. –

Antwort

11

Sie können apache's mod_negotiation module nutzen. Auf diese Weise können Sie die volle Bandbreite an Verhandlungsfunktionen des Moduls nutzen, einschließlich Ihrer eigenen Einstellungen für den Inhaltstyp (zB "Ich möchte wirklich application/xhtml + xml bereitstellen, es sei denn, der Kunde bevorzugt etwas anderes "). Basislösung:

  • erstellen eine .htaccess Datei mit
    AddHandler type-map .var
    als Inhalt
  • eine Datei foo.var erstellen mit
    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
    als Inhalt
  • eine Datei foo.php mit
    <?php 
    echo 'selected type: ', substr($_SERVER['PATH_INFO'], 1);
    als Inhalt erstellen. http://localhost/whatever/foo.var
  • Anfrage

Damit dies funktioniert Sie mod_negotiation aktiviert, werden die entsprechenden AllowOverride Privilegien für AddHandler und AcceptPathInfo nicht deaktiviert wird für $ _SERVER [ 'PATH_INFO'] müssen.
Mit meinem Firefox senden "Akzeptieren: text/html, application/xhtml + xml, application/xml; q = 0.9, /; q = 0.8" und das Beispiel .var map das Ergebnis ist "ausgewählter Typ: xhtml" .
Sie können andere "Tweaks" verwenden, um PATH_INFO oder die Anforderung von foo .var loszuwerden, aber das Grundkonzept lautet: Lassen Sie mod_nevertiation die Anfrage so an Ihr PHP-Skript umleiten, dass das Skript "lesen" kann der ausgewählte Inhaltstyp

So, weiß jemand von einem bewährten Stück Code PHP
auswählen Es ist nicht eine reine PHP-Lösung, aber ich würde sagen, mod_negotiation wurde versucht und getestet ;-)

+0

Akzeptiert - Noch besser als PHP. – l0b0

+3

Ich würde es vorziehen, diese Logik in meinem Code zu behalten. – Greg

19

Kleiner Ausschnitt aus meiner Bibliothek:

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

Beispiel Nutzung:

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

nur eine kleine Verbesserung: Ändern Sie den Funktionsprototyp in 'function getBestSupportedMimeType ($ mimeTypes = null, $ acceptedTypes = FALSE) {if ($ acceptedTypes === FALSE) {$ acceptedTypes = $ _SERVER ['HTTP_ACCEPT']; } ... '. Im Wesentlichen erlauben benutzerdefinierte Typen akzeptieren, wenn das Programm etwas mehr benutzerdefinierte tun muss. – chacham15

10

Birne :: HTTP 1.4.1 hat ein Verfahren 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"; 
} 

druckt

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

bearbeiten : das könnte doch nicht so gut sein ...
My firefox sendet Accept: text/html, application/XHTML + xml, application/xml; q = 0,9, /; q = 0,8
text/html und application/XHTML + xml haben q = 1,0, aber PEAR :: HTTP (afaik) lässt nicht Sie wählen Sie, welche Sie bevorzugen, es gibt Text/HTML zurück, egal was Sie als $ unterstützt übergeben. Dies kann oder kann nicht für Sie ausreichend sein. Siehe meine anderen Antworten.

+1

Für PHP 5-Code verwenden Sie das HTTP2-Paket: http://pear.php.net/package/HTTP2 – cweiske

9

Nur für das Protokoll Negotiation ist eine reine PHP-Implementierung für die Inhaltsverhandlung.

1

Zusammengeführt @ maciej-Łebkowski und @ chacham15 Lösungen mit meinen Problemen behoben und Verbesserungen. Wenn Sie $desiredTypes = 'text/*' übergeben und Accepttext/html;q=1 enthält, wird text/html zurückgegeben.

/** 
* 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

Sie dürfen q = 0 aus den Accept-Kopfzeilen des Clients nicht herausfiltern. Dies bedeutet, dass der Client diesen Typ nicht akzeptiert, z. "Accept-Language: en, en-US; q = 0" bedeutet, dass ich kein Englisch akzeptieren werde, solange es nicht amerikanisch ist. –

0

Der Client akzeptiert möglicherweise eine Liste der MIME-Typen in der Antwort. Auf der anderen Seite ist die Reihenfolge der Antwort sehr wichtig für die Kundenseite. PHP Pear HTTP2 ist das beste, um mit Sprache, Zeichensatz und MIME-Typen umzugehen.

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

ist hier ein gutes Tutorial: https://cweiske.de/tagebuch/php-http-negotiation.htm