Content Negotiation

コンテント・ネゴシエーションについて。

コンテント・ネゴシエーションとは?

HTTPによる資源提供の仕組みにおいて、それを受け取る側となる全ての利用者は嗜好等が同じではないし、全てのUAが全ての情報を等しく解析し表現できるわけではない。つまり提供者側は、受け手の利用者側の要求に対応した最も利用可能な表現をユーザに供給するのが望ましい。

コンテント・ネゴシエーションとは、複数の異なる(例えば、英語と日本語といった異なる言語)表現がある時にそれに応じた最適な表現を提供する為の処理の仕組みのことで、HTTPでは、サーバ駆動型ネゴシエーションエージェント駆動型ネゴシエーションの二種類のコンテント・ネゴシエーションが可能である。

昨今、世界的なインターネットの爆発的な普及と共に、様々な人が様々な場所からHTTPに接続するようになり、利用者側の環境も多種多様となった。また、提供する側も情報資源の表現の形式(HTMLやXHTML、gzipなどの圧縮など)等が多様化し、例えば、コンテンツによってはUA側で処理可能なインタプリタ言語(JavaScript等)をHTMLに埋め込むことでOSやブラウザの種類、またそのバージョンなどを判別して異なる表現へと導く処理(クライアント駆動型ネゴシエーション)を行っている場合も見受けられる。

コンテントネゴシエーションクラス

クラスの作成経緯

本サイトでは、サーバサイドXSLT変換を行う際の変換結果(出力)の選択に [RFC2616] で定義されているコンテント・ネゴシエーションという仕組みを取り入れようとこのクラスを作成しました。

ContentNegotiationクラス

public class ContentNegotiation extends Object

構造

メソッド

PHP 5
public void addTypeMap(String uri [, String media_type [, String charset [, String language [, String qvalue [, String description]]]]])
タイプマップ(バリアントの情報)の追加。
引数
String uri
ファイルのURI
String media_type
メディアタイプ [media-type]
メディアタイプが 'text/html' の場合のみ、";" 区切りでメディアタイプのバージョン(level)を指定可能
String charset
文字セット [charset]
String language
言語セット [language-tag]
String qvalue
品質係数 [qvalue]
0 から 1 までの浮動小数点(小数点以下三桁まで)、0 は最小値で 1 は最大値
String description
バリアントを説明した文章
public void httpResponseHeader([Boolean $body])
HTTPレスポンスヘッダを送信する。
引数
Boolean $body
HTMLコードの生成の可否。httpResponseHeaderメソッドは、返すべき資源(適切なバリアント)が無いと判断した場合に、”406 (Not Acceptable) ステータスコード”を返し、全てのバリアントの一覧を提供できます。また、ネゴシエーション実行以前なら、"300 Multiple Choices" を送信します。
public Array negotiate([String Accept [, String Accept_Charset [, String Accept_Language]]])
ネゴシエーションによる最適な表現の選択。
引数
String Accept
Acceptリクエストヘッダフィールド
String Accept_Charset
Accept-Charsetリクエストヘッダフィールド
String Accept_Language
Accept-Languageリクエストヘッダフィールド
引数を指定することもできますが、これはデバッグ作業を考慮してのものです。動作確認の際などに引数を指定する場合には、[RFC2616] Hypertext Transfer Protocol -- HTTP/1.1 の書式で各値を設定できます。
返り値
ネゴシエーションアルゴリズムの処理の結果、最適なバリアントがあればその情報を配列で、何も選択されなかった場合には FALSE を返します。
PHP 4
public void addTypeMap(String uri [, String media_type [, String charset [, String language [, String qvalue [, String description]]]]])
タイプマップ(バリアントの情報)の追加。
引数
String uri
ファイルのURI
String media_type
メディアタイプ [media-type]
String charset
文字セット [charset]
String language
言語セット [language-tag]
String qvalue
品質係数 [qvalue] (※0 から 1 までの浮動小数点(小数点以下三桁まで)、0 は最小値で 1 は最大値)
String description
バリアントを説明した文章
public void httpResponseHeader([Boolean $status_code])
HTTPレスポンスヘッダを送信する。
引数
Boolean $status_code
ステータスコード要素の送信の可否。httpResponseHeaderメソッドは、返すべき資源(適切なバリアント)が無いと判断した場合に、”406 (Not Acceptable) ステータスコード”を返し、全てのバリアントの一覧を提供できます。$status_code変数を FALSE にすることでその表示を抑制できますが推奨致しません。
public Array negotiate([Boolean Accept [, Boolean Accept_Charset [, Boolean Accept_Language]]])
ネゴシエーションによる最適な表現の選択。
引数
Boolean Accept
Acceptリクエストヘッダフィールドによる推測の可否。
Boolean Accept_Charset
Accept-Charsetリクエストヘッダフィールドによる推測の可否。
Boolean Accept_Language
Accept-Languageリクエストヘッダフィールドによる推測の可否。
返り値
ネゴシエーションアルゴリズムの処理の結果、最適なバリアントがあればその情報を配列で、何も選択されなかった場合には FALSE を返します。

クラスの使用例

次の例は、ContentNegotiationクラスを使用したサンプルです。

<?php
ini_set("include_path", "./");
// require_once("org/purl/net/osamurai/http/ContentNegotiation.php"); // for PHP 4
require_once("http/ContentNegotiation.php"); // for PHP 5

$cn = new ContentNegotiation;
// type-map: URI, media-type, charset, language-tag, qvalue, description
$cn->addTypeMap("foo");
$cn->addTypeMap("foo.html", "text/html");
$cn->addTypeMap("foo.ja.html.sjis", "text/html", "shift_jis", "ja");
$cn->addTypeMap("foo.en.html", "text/html", "iso-8859-1", "en");
$cn->addTypeMap("foo.ja.xhtml.utf8", "application/xhtml+xml", "utf-8", "ja", "0.9");
$cn->addTypeMap("foo.txt", "text/plain", "", "", "0.8");
$cn->addTypeMap("foo.dc.rdf", "application/rdf+xml", "utf-8", "", "0.5", "Dublin Core Metadata [RDF/XML]");
$result = $cn->negotiate();
$cn->httpResponseHeader();

if (!empty($result['uri'])) {
  $filename = $result['uri'];
  $handle = fopen($filename, "r");
  $contents = fread($handle, filesize($filename));
  fclose($handle);
  print($contents);
}

unset($cn);
?>

上記の様にして、単に複数のファイルからひとつを選択する処理に用いる場合には、サーバがコンテントネゴシエーションの機能をサポートしているようならば、そちらの機能を使用した方がパフォーマンスは良いかと思います。

PHPを用いて、HTTP GET 及び POST やクッキー等の情報を利用して最適なバリアントを選択させたい場合には、次の記述の様にしてタイプマップを追加すると良いでしょう。

...
// 例)ファイルのURIにクエリー文字を含む場合
$cn->addTypeMap("foo.php", "", "", "", "", "Muliple");
$cn->addTypeMap("foo.php?type=html401", "text/html"); // HTML 4.01
$cn->addTypeMap("foo.php?type=xhtml10", "text/html", "", "", "0.9"); // XHTML 1.0
$cn->addTypeMap("foo.php?type=xhtml10", "application/xhtml+xml"); // XHTML 1.0
$cn->addTypeMap("foo.php?type=xhtml1", "application/xhtml+xml", "", "", "0.9"); // XHTML 1.1
$cn->addTypeMap("foo.php?type=xhtml2", "application/xhtml+xml", "", "", "0.5"); // XHTML 2 (WD)
...

ちなみに、このURIの全てのバリアントの一覧を確認することもできます。

注釈