urlに+(plus)とapacheのrewriteRule

webサイトを開発していると、
SEOとか静的URLとか
リソース指向だとか
URIをどうするかいろいろ設計すると思います。

なんらかのフレームワークに乗っかっている場合、
そのフレームワークのルールのまんまいくときもあるとおもいます。

ただその場合、コントローラだのアクションだの名前がついてしまいますよね。

それ以外だとapacheのrewriteRuleを用いて、
URIを定義するかと思います。



で前置きはここまでにして、
http://delicious.com/toshifumi_tegu/apache+expires
こういうdeliciousみたいなQUERYをプラスでつなぐような
URIをつくらないといけなくなったときに
apacheのrewriteRuleをつかって
はまったことをまとめておきます。


そもそもプラスってURI的には「?」などと同じ予約文字でした。

出典 http://www.studyinghttp.net/cgi-bin/rfc.cgi?3986#Sec2.2

そりゃそうですよね。phpでurlencodeすると空白は+に変わります。(rawurlencodeなら%20ですが)

だから別にphp側で空白で取れようが、+で取れようが、どっちでもいいはずなんです。

しかしQUERYに+が含まれる場合(「C++」とか)、この場合区別できないといけません。
そこで諸所のサイトでは区切り文字ではない+はエンコードして渡すということになっていたりします。

で、別にapacheのリライトルールを使わなければ、普通にそれを区別して取得することができました。


でもクールなURIにはならないので

以下のようなルールを書くかと思います。(今回使用したフレームワークの場合)

^/path-to/([^/]+)$    /var/www/index.php?controller=foo&action=bar&query=$1

これでいいような感じがするんですが、
「A+」(エンコードするとA%2B)と「B」をqueryに渡そうとすると
http://domain/path-to/A%2B+B
$_GET['query']の中には「A B」とスペース二個に変換されています。
$_SERVER['REQUEST_URI']内では「A++B」として入っています。
なくなるか区別できなくなるか。

apacheのRewriteLogを見てみると
rewriteされている時点で$1はA++Bになっています。



こりゃ困った。簡単にできるような気がしていたのに全然あかんやんっていう。


そこで発想の転換

以下の作者である先輩がRwriteしなきゃいいじゃんと教えてくれました。
http://freestyle.xrea.jp/blog/article.php?id=20

種々のフレームワークだと上記のように引数がPATHフォーマットでも大丈夫らしいです。

今回利用していたフレームワークはこちらには対応していなかったため、
対応するように変更。

1.まずエントリーポイントのindex.phpをpatht-toってファイル名でコピー
cp /www/var/index.php  /www/var/path-to
2. apache.htaccessを以下のように変更
#^/path-to/([^/]+)$    /var/www/index.php?controller=foo&action=bar&query=$1

<Directory /var/www>
    <FilesMatch "^path-to$">
        ForceType application/x-httpd-php 
    </FilesMatch>
</Directory>
3. さきほどコピーしたファイルpath-to内で
<?php
$_GET['controller'] = 'foo';
$_GET['action'] = 'bar';
$_GET['query'] = explode('+', str_replace('/path-to/', '', $_SERVER['REQUEST_URI']));

//以下通常のフレームワークのエントリーポイントと同じ

このようにすることによって意図したqueryが得れます。


ただし$_SERVER['REQUEST_URI']はデコードされていない

よってqueryもデコードされておらず、自分でデコードする必要があります。
デコードした場合、
http://www.php.net/manual/ja/filter.filters.sanitize.php
フィルターなどを利用して普段はフィルターしていても
デコードされていないため、タグなども除去されていません。
自分でデコードしてあやまってscriptタグなどを表示しないよう
再度htmlspecialcharsを実行するなどして
http://php.net/manual/ja/function.htmlspecialchars.php
セキュリティ面に考慮する必要があります。


ということで上記のような対応で、deliciousのタグ検索みたいなURIのページが生成できました。