2009-06-26 31 views
17

Tôi đang cố gắng xây dựng một khung trang web tuân thủ chuẩn phục vụ XHTML 1.1 dưới dạng application/xhtml + xml hoặc HTML 4.01 dưới dạng văn bản/html tùy thuộc vào hỗ trợ của trình duyệt. Hiện tại, nó chỉ tìm kiếm "application/xhtml + xml" ở bất kỳ đâu trong header chấp nhận và sử dụng nếu nó tồn tại, nhưng điều đó không linh hoạt - text/html có thể có điểm số cao hơn. Ngoài ra, nó sẽ trở nên phức tạp hơn khi các định dạng khác (WAP, SVG, XForms vv) được thêm vào. Vì vậy, không ai biết về một đoạn mã PHP đã thử và thử nghiệm để chọn, từ một mảng chuỗi do máy chủ đưa ra, hoặc là chuỗi được hỗ trợ tốt nhất bởi máy khách hoặc danh sách có thứ tự dựa trên điểm số của khách hàng?Cách chọn loại nội dung từ tiêu đề HTTP Accept trong PHP

+0

Trong khi đó là tất cả tốt để thử và có tiêu chuẩn phù hợp và "làm những điều đúng", tôi nghĩ rằng nó có giá trị chi tiêu một thời điểm xem xét nếu bạn thực sự sẽ đạt được bất kỳ lợi ích từ tất cả điều này. Ví dụ. không có nhiều lý do để cung cấp ứng dụng/xhtml + xml khi văn bản/html hoạt động tốt và vân vân. –

Trả lời

11

Bạn có thể tận dụng apache's mod_negotiation module. Bằng cách này, bạn có thể sử dụng đầy đủ các khả năng thương lượng mà mô-đun cung cấp, bao gồm tùy chọn của riêng bạn cho loại nội dung (e, g, "Tôi thực sự muốn phân phối ứng dụng/xhtml + xml, trừ khi khách hàng thích nhiều thứ khác "). giải pháp cơ bản:

  • tạo một file .htaccess với
    AddHandler type-map .var
    như nội dung
  • tạo ra một tập tin với 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
    như nội dung
  • tạo ra một tập tin với foo.php
    <?php 
    echo 'selected type: ', substr($_SERVER['PATH_INFO'], 1);
    như nội dung.
  • yêu cầu http://localhost/whatever/foo.var

Để làm việc này bạn cần kích hoạt mod_negotiation, những đặc quyền AllowOverride thích hợp cho AddHandler và AcceptPathInfo không được vô hiệu hóa với $ _SERVER [ 'PATH_INFO'].
Khi Firefox của tôi gửi "Chấp nhận: văn bản/html, ứng dụng/xhtml + xml, ứng dụng/xml; q = 0,9, /; q = 0,8" và ví dụ .var ánh xạ kết quả là "loại đã chọn: xhtml" .
Bạn có thể sử dụng "tinh chỉnh" khác để loại bỏ PATH_INFO hoặc cần phải yêu cầu foo .var, nhưng khái niệm cơ bản là: để mod_negotiation chuyển hướng yêu cầu tới tập lệnh php của bạn theo cách kịch bản có thể "đọc" loại nội dung được chọn.

Vì vậy, không ai biết của một mảnh thử và thử nghiệm mã PHP để chọn
Nó không phải là một giải pháp php tinh khiết nhưng tôi muốn nói mod_negotiation đã được thử và thử nghiệm ;-)

+0

Đã chấp nhận - Thậm chí tốt hơn so với PHP. – l0b0

+3

Tôi muốn giữ logic này bên trong mã của mình. – Greg

19

Ít đoạn từ thư viện của tôi:

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

ví dụ sử dụng:

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

chỉ là một cải tiến tuổi teen: thay đổi nguyên mẫu hàm thành 'hàm getBestSupportedMimeType ($ mimeTypes = null, $ acceptedTypes = FALSE) {if ($ acceptedTypes === FALSE) {$ acceptedTypes = $ _SERVER ['HTTP_ACCEPT']; } ... '. về cơ bản cho phép các loại chấp nhận tùy chỉnh nếu chương trình cần phải làm điều gì đó tùy chỉnh hơn một chút. – chacham15

10

Pear :: HTTP 1.4.1 có một phương pháp 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"; 
} 

in

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

chỉnh sửa : điều này có thể không tốt cho lắm ...
firefox của tôi gửi Chấp nhận: text/html, application/xhtml + xml, application/xml; q = 0,9, /; q = 0,8
text/html và application/xhtml + xml có q = 1,0 nhưng PEAR :: HTTP (afaik) không cho phép bạn chọn cái nào bạn thích, nó trả về văn bản/html bất kể bạn chuyển qua $ nào. Điều này có thể hoặc có thể không đủ cho bạn. xem (các) câu trả lời khác của tôi.

+1

Đối với mã PHP 5, sử dụng gói HTTP2: http://pear.php.net/package/HTTP2 – cweiske

9

Chỉ cần cho các hồ sơ , Negotiation là triển khai PHP thuần túy để xử lý thương lượng nội dung.

1

Đã hợp nhất các giải pháp @ maciej-Łebkowski và @ chacham15 với các vấn đề và cải tiến của tôi. Nếu bạn vượt qua $desiredTypes = 'text/*'Accept chứa text/html;q=1 thì text/html sẽ được trả lại.

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

Bạn không được lọc ra q = 0 từ máy khách Chấp nhận tiêu đề. Điều đó có nghĩa là khách hàng sẽ không chấp nhận loại đó, ví dụ: 'Accept-Language: en, en-US; q = 0' có nghĩa là tôi sẽ chấp nhận bất kỳ tiếng Anh nào miễn là nó không phải là người Mỹ. –

0

Khách hàng có thể chấp nhận danh sách loại mime trong phản hồi. Mặt khác, thứ tự của phản ứng rất quan trọng đối với phía khách hàng. PHP Pear HTTP2 là cách tốt nhất để xử lý ngôn ngữ, bộ ký tự và 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"; 
} 

Đây là một hướng dẫn tốt: https://cweiske.de/tagebuch/php-http-negotiation.htm

Các vấn đề liên quan