Notes :: 2006年02月

最新版

最近書かれた記事5件分を表示します。

更新情報RDF

更新状況をRSS 1.0形式でご覧になれます。

卒業研究ことはじめ

URI
http://rhongomyniad.org/notes/2006/02.html#d24t2357
カテゴリ
雑多
投稿日時
2006-02-24 23:57

数日前から、来年度の卒業研究へ向けて動き出している。研究テーマは先輩から引き継いだものであり(ありがとうございます)、簡潔には、簡易データベースとWebサイトの制作だと表現できる。

とはいえ、研究テーマをそっくりそのまま引き継ぐのではなく、自分なりに潜在的なテーマを見出して取り掛かっている。つまり、データベースとWebサイトの制作を引き継ぎつつ、両者の根底にある要素、例えば、XMLやXSLT(データベースシステムの構築)、ユーザビリティ(Webサイトの使いやすさ)を究めるという意味だ。

ユーザビリティなどの研究から得られたものを制作に活かし、扱っているデータからというよりは、別の観点から完成度を高めたいと考えている。

興味のある分野に通じる研究テーマを引き継げるというのは、本当にありがたいことだ。最後まできっちりとやり遂げたい。

更新日時とETagによる条件付きGET

URI
http://rhongomyniad.org/notes/2006/02.html#d15t0153
カテゴリ
PHP
Webサイト制作
投稿日時
2006-02-15 01:53

2005年10月17日に条件付きGETを導入したものの、実際にはきちんと動作していなかった。その当時からWebサイトの構造を変更している(XHTML+PHP→XML+XSLT+PHP)こともあり、もう一度見直すことにする。

前回同様、Arielworksのモジュール版PHPで「If-Modified-Since」に対応するという記事を参考にした。特に「日付の解析」部分は、示されているソースコードをそのまま組み込んでいる。改めて感謝の意を表したい。

UNIXタイムスタンプ

UNIXタイムスタンプとは、1970年1月1日0時0分0秒(GMT)からの通算経過秒のことである。例えば、2006年2月13日18時57分15秒(JST)は、UNIXタイムスタンプでは1139824635(秒)と表す。

2006年2月13日といった形式では日付の大小を比較できない(あるいは非効率)ため、UNIXタイムスタンプを用いて比較する。日付の比較とは、もちろん、更新の有無を判断するためのものだ。

PHP関数のfilemtime()を用いると、リソースの更新日時がUNIXタイムスタンプで得られる。

$xml = "../foo/bar.xml";
$xml_modified_timestamp = filemtime($xml);

Last-Modifiedの送信

HTTPレスポンスヘッダのLast-Modifiedフィールド値には、「Sun, 13 Feb 2006 09:57:15 GMT」といった一般的な形式の更新日時を入れる。gmdate()でUNIXタイムスタンプを変換してやればよい。

$xml_modified = gmdate("D, d M Y H:i:s", $xml_modified_timestamp)." GMT";

HTTPレスポンスヘッダのLast-Modifiedフィールドを送信するには、次のように記述する。

header("Last-Modified: ".$xml_modified);

その結果、サーバから返されるレスポンスヘッダは次のようになる。

HTTP/1.0 200 OK
Date: Mon, 13 Feb 2006 11:51:31 GMT
Server: Apache
X-Powered-By: PHP/4.3.11
Last-Modified: Sun, 12 Feb 2006 14:28:04 GMT
ETag: 2f972ec925d4f9ceb2d2c530a510a6aa
Content-Encoding: gzip
Vary: Accept-Encoding
Content-Length: 1401
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/xhtml+xml; charset=UTF-8

ETagの生成と送信

ETagとは、ファイルサイズや更新日時から生成される、コンテンツを特定するためのユニークな値のことである。32バイト長の文字列だと思えばよい。

ETag: 2f972ec925d4f9ceb2d2c530a510a6aa

ファイルの更新日時をETagの構成要素に含めると、当然ながら、ファイルを更新する度に異なるETagが生成される。つまり、次に挙げる2つのETagが一致しなければ、更新があったと見なすことができる。

ETagは、md5()を用いたMD5ハッシュ値として生成する。$_SERVER["REQUEST_URI"]と更新日時からETagを生成するには、次のようにすればよい。

$etag = md5($_SERVER["REQUEST_URI"].$xml_modified);

Last-Modifiedフィールドと同様に、ETagフィールドは次のようにして送信する。

header("ETag: ".$etag);

条件付きGET

Last-ModifiedとETagを送信するようにしたら、クライアントから送られてくるIf-Modified-SinceとIf-None-Matchを受け取る部分に取り掛かる。

If-Modified-Since

Last-Modifiedと一緒に使う。前回受け取ったLast-Modifiedの値をIf-Modified-Sinceの値として示し、この日時以降に更新されていればコンテンツを送るように伝える。

If-Modified-Since: Sun, 12 Feb 2006 14:28:04 GMT
If-None-Match

ETagと一緒に使う。前回受け取ったETagの値をIf-None-Matchの値として示し、サーバのETagと一致しなければコンテンツを送るように伝える。

If-None-Match: 2f972ec925d4f9ceb2d2c530a510a6aa

コンテンツを送信するか否かを判断する部分は、次のようにしている。

require("if-modified-since.php");
$request_headers = apache_request_headers();
if ($request_headers["If-Modified-Since"]) {
    $client_time = parse_http_date($request_headers["If-Modified-Since"]);
    if ($client_time["timestamp"] >= $xml_modified_timestamp) {
        http304();
    }
} elseif ($request_headers["If-None-Match"]) {
    $client_etag = $request_headers["If-None-Match"];
    if ($client_etag == $etag) {
        http304();
    }
}
  1. Arielworksの「日付の解析」を読み込む
  2. apache_request_headers()でクライアントからのリクエストヘッダを取得する
  3. If-Modified-Sinceフィールドが送られていれば:
    1. フィールド値を「日付の解析」に通してUNIXタイムスタンプ形式へ変換する
    2. リソースの更新日時と比較する
    3. リソースの更新日時と同じか、それより大きければHTTP 304を返して終了する
  4. If-None-Matchフィールドが送られていれば:
    1. サーバのETagと比較する
    2. ETagと一致すれば、HTTP 304を返して終了する

If-Modified-SinceとIf-None-Matchの一方にしか対応していないUser Agentを考慮し、二重の処理を施しておくとよい。なお、HTTP 304(「更新はありません」)を返す関数は別個に用意しておく。

function http304() {
    global $xml_modified, $etag;
    header("HTTP/1.1 304 Not Modified");
    header("Last-Modified: ".$xml_modified);
    header("ETag: ".$etag);
    exit();
}

HTTP 304と一緒にLast-ModifiedとETagを送信した後、exit()でスクリプトを終了する。http304()以降にコンテンツ出力の部分を記述しているため、必ずexit()で終了しなければならない。そうしないと、HTTP 304を返しながらコンテンツを送るというおかしなことになってしまう。

HTTP_ACCEPT: application/xhtml+xml

URI
http://rhongomyniad.org/notes/2006/02.html#d08t2103
カテゴリ
PHP
Web全般
Webサイト制作
投稿日時
2006-02-08 21:03

User Agentの中には、自身が解釈できないにもかかわらず、HTTPリクエストヘッダのHTTP_ACCEPTフィールド値の一つとしてapplication/xhtml+xmlを送るものがある。HTTP_ACCEPTフィールド値を見て、コンテンツのContent-Type(MIME type)を決めているのであれば注意が必要だ。

当サイトでは、PHPを用いてXHTML文書のContent-Typeを決めている。

$accept = $_SERVER['HTTP_ACCEPT'];
if (ereg("application/xhtml\+xml", $accept)) {
    header("Content-Type: application/xhtml+xml; charset=UTF-8");
} else {
    header("Content-Type: text/html; charset=UTF-8");
}

HTTP_ACCEPTフィールド値にapplication/xhtml+xmlを含んでいればその形式で返し、さもなくばtext/htmlで返す。User Agentが解釈できるとするContent-Typeで返すため、処理・表示は問題なくできるはずである。だが、Googlebotにおいてはこの限りではない。

Googlebotはapplication/xhtml+xmlに対応しているのか

Content-Typeの処理を以上のものに変更してからは、Googleの検索結果のsnippetが空になったり、キャッシュの内容が書き換えられなかったりしていた(クローラは来ている)。それ以前はOperaとGecko系ブラウザに対してのみapplication/xhtml+xmlで返していたため、この変更が影響しているのは明白である。

Googlebotは、application/xhtml+xmlに対応していないそうだ。だが、HTTP_ACCEPTフィールド値にはapplication/xhtml+xmlが含まれている。この点に注意しなければならない。

これを回避するには、HTTP_USER_AGENTを見て処理を変えればよい。例えば、次のようにする。

$useragent = $_SERVER['HTTP_USER_AGENT'];
if (eregi("bot", $useragent) || eregi("crawl", $useragent)) {
    $client = "bot";
} else {
    $client = "browser";
}

HTTP_USER_AGENTフィールド値にbotかcrawlという文字列(大文字小文字は区別しない)が含まれていればbotとして扱い、それ以外は便宜上browserとする。分岐条件は、必要に応じて増やしてやるとよい。

$accept = $_SERVER['HTTP_ACCEPT'];
if (ereg("application/xhtml\+xml", $accept) && $client != "bot") {
    header("Content-Type: application/xhtml+xml; charset=UTF-8");
} else {
    header("Content-Type: text/html; charset=UTF-8");
}

HTTP_ACCEPTフィールド値にapplication/xhtml+xmlを含んでいて、かつbotと判定されていなければ、コンテンツをapplication/xhtml+xmlで返す。基本の流れは以上の通りだ。

Googleの機能を利用したサイト内検索

URI
http://rhongomyniad.org/notes/2006/02.html#d01t0349
カテゴリ
Webサイト制作
投稿日時
2006-02-01 03:49
更新日時
2006-02-08 18:37

Googleの機能を利用する、サイト内検索をしばらく前から設置している。ただ設置するだけなら簡単だが、どのような引数が利用できるかくらいは知っておきたい。

基本的な設置方法については、リンク、検索機能を追加(Google)を参照すること。以下、サイト内検索に利用している引数を覚え書きしておく。

www.rhongomyniad.orgの検索結果の削除を申請したところ、rhongomyniad.orgまで削除されていた。Google Sitemapでは両者は区別されているが、削除申請では同一のものと扱われてしまったようだ。

よって、現在はサイト内検索を取り外している。

2006-02-08 18:37追記

sitesearch

引数にsitesearchを加えてやると、特定のドメイン以下にあるリソースのみを検索対象にできることが知られている。

<input type="hidden" name="sitesearch" value="rhongomyniad.org" />

一つ注意が必要なのは、サブドメインをも検索対象に入れてしまうことだ。上記の指定では、Web技術とは別段関係のないblade.rhongomyniad.org以下のリソースまで対象に入れられてしまう。

as_eq

blade.rhongomyniad.org以下のリソースを除外するため、not演算を行なう引数as_eqを使うことにした。

<input type="hidden" name="as_eq" value="inurl:blade.rhongomyniad.org"/>

inurl:を頭に付与しておくと、以降の文字列をURIに含むリソースのみが除外される。つまり、URIにblade.rhongomyniad.orgを含んでいなければ、文書内に何度書こうとそのリソースは除外されない。

domains

引数domainsを入れておくと、サイト内検索の結果から「WWWを検索」できるようになる。満足できる結果が得られなかったら、「WWWを検索」というラジオボタンにチェックを入れて検索し直すためのものだ。多少は便利だろうと思う。

<input type="hidden" name="domains" value="rhongomyniad.org"/>

「-inurl:blade.rhongomyniad.org」という余計な検索語句まで条件に含められてしまうが、検索結果に影響することはまずないと言える。

参考文献

文書情報

桐沢 辰