CakePHPのURLエンコード
CakePHPではapp/webroot/.htaccessに
RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]
という記述があり、オリジナルのURLを$_GET['url']にいれた状態でRouterに渡す。
ここで、問題が発生するのがオリジナルのURLに+や%40などのエスケープされた文字が入っていた場合、mod_rewrite上で案エスケープされてしまう為、元の文字列が取得できない。
もっともシンプルな解決法はApacheの2.2.6以降に準備されているRewrite RuleのBフラグ。
'B' (escape backreferences)
Apache has to unescape URLs before mapping them, so backreferences will be unescaped at the time they are applied. Using the B flag, non-alphanumeric characters in backreferences will be escaped. For example, consider the rule:RewriteRule ^(.*)$ index.php?show=$1
This will map /C++ to index.php?show=/C++. But it will also map /C%2b%2b to index.php?show=/C++, because the %2b has been unescaped. With the B flag, it will instead map to index.php?show=/C%2b%2b.This escaping is particularly necessary in a proxy situation, when the backend may break if presented with an unescaped URL.
とあるように、先ほどの.htaccessの最後の行を
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L,B]
とすればエスケープしてくれる。
残念ながらApahceを2.2.6以上にできない場合はhttpd.confを触れれば
下記の様な解決法も。
httpd.confで
RewriteLock /tmp/httpd.rewrite.lock RewriteMap urlencode prg:/etc/httpd/conf.d/bin/urlencode.pl
としておいて.htaccessは
RewriteRule ^(.*)$ index.php?url=${urlencode:$1} [QSA,L]
RewriteMapで外部スクリプトにエンコードさせるわけです
今回は手っ取り早く下記Perlスクリプトを試しましたが速度的にはオススメできません
#!/usr/bin/perl $| = 1; while (<STDIN>) { s/\n$//; s/([^\w ])/'%'.unpack('H2', $1)/eg; tr/ /+/; print "$_\n"; }