If-Modified-Since 戦争が終結
PHP で Apache 風 ETag の生成などしていたのですが、ちょっと方法が甘くて、3 種類あるとされる HTTP-date のうち ANSI C 形式 ( e.g.: Sun Nov 6 08:49:37 1994 ) には対応できていませんでした。
そんな折、Note @ Temporary-Depot の「バナーキャッシュ php 版(あるふぁ)」というエントリを読んで、ANSI C 形式にも考慮した 2 行のコードを提案させていただいたところ、「If-Modified-Since を UNIX タイムに変換したいだけなんだ」というエントリで詳細に上手くまとめてくださったのでした。
<?php if ( !strpos( $string_date, ',' ) ) $string_date .= ' GMT';
return strtotime( $string_date ); ?>
このケースでは、カンマが頭にくることはありえないけど !strpos() より strpos() === false の方が良いかもしれない……
それからしばらくして最近のこと、avoidnote の「If-Modified-Since に対応してみる」というエントリを読みまして、セミコロンと共に不正な値を付け加えるクライアントの存在とその対処方法を知りました。
つーことでこれらを踏まえて、Apache 風 ETag 生成 PHP コードを鍛え直す。前述の各エントリに記されているコードが参考になりました。自分好みにマージします。
<?php
function str2time( $str ) {
$str = preg_replace( '/;.*$/', '', $str );
if ( strpos( $str, ',' ) === false )
$str .= ' GMT';
return strtotime( $str );
}
function do_conditional_get( $timestamp = false ) {
if ( !empty( $_SERVER['QUERY_STRING'] ) ) {
header( 'Cache-Control: no-store, no-cache, must-revalidate' );
header( 'Cache-Control: pre-check=0, post-check=0', false );
return;
}
if ( !$timestamp )
$timestamp = getlastmod();
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s T', $timestamp ) );
$stats = stat( $_SERVER['SCRIPT_FILENAME'] );
$etag = sprintf( '"%x-%x-%x"', $stats['ino'], $stats['size'], $stats['mtime'] );
header( 'ETag: ' . $etag );
$if_modified_since = isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ?
str2time( stripslashes( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ): false;
$if_none_match = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ?
stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ): false;
if ( !$if_modified_since && !$if_none_match )
return;
if ( $if_none_match && $if_none_match != $etag )
return;
if ( $if_modified_since && $if_modified_since < $timestamp )
return;
header( 'HTTP/1.1 304 Not Modified' );
exit();
}
do_conditional_get();
?>
header() は HTTP_Header パッケージで置き換えた方がスマートだろうか。とりあえず保留。
暇をみて pecl_http パッケージを試せたら、このコードは捨ててそちらを使いたい。Last-Modified だけでなく ETag にも対応しているみたいなので。
以上、終わり。
以下 stab. include や require などで複数のファイルから構成される PHP スクリプトに対応…… 途中で放り出し。
<?php
function do_conditional_get( $timestamp = false, $files = false ) {
if ( !empty( $_SERVER['QUERY_STRING'] ) ) {
header( 'Cache-Control: no-store, no-cache, must-revalidate' );
header( 'Cache-Control: pre-check=0, post-check=0', false );
return;
}
if ( is_array( $files ) ) {
array_flip( $files );
$files[$_SERVER['SCRIPT_FILENAME']] = getlastmod();
foreach ( $files as $k => $v ) {
if( file_exists( $k ) )
$files[$k] = filemtime( $k );
}
sort( $files, SORT_NUMERIC );
$timestamp = array_pop( $files );
} else if ( !$timestamp )
$timestamp = getlastmod();
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s T', $timestamp ) );
$stats = stat( $_SERVER['SCRIPT_FILENAME'] );
$etag = sprintf( '"%x-%x-%x"', $stats['ino'], $stats['size'], $stats['mtime'] );
header( 'ETag: ' . $etag );
$if_modified_since = isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ?
str2time( stripslashes( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) ): false;
$if_none_match = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ?
stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ): false;
if ( !$if_modified_since && !$if_none_match )
return;
if ( $if_none_match && $if_none_match != $etag )
return;
if ( $if_modified_since && $if_modified_since < $timestamp )
return;
header( 'HTTP/1.1 304 Not Modified' );
exit();
}
?>
……駄目だこりゃ。
- タグ
- Apache
- ETag
- PHP
- 公開日時
- 2005-07-08T23:22:12+09:00 @640
- Permalink URI & TrackBack URL
- http://blog.drry.jp/2005/07/08/2322
コメント