CodeIgniter側でさくらインターネットのmod_rewriteの動きを自動で吸収する

さくらインターネットmod_rewriteが上手くいかないのは結構よくある話で、私も悩んだクチです。
自動とはいかないまでも、半自動でいいからアプリケーション側で吸収できたら幸せになれそうです。

そもそもの発端

「index.php/topとかついてるのはカッコ悪い。だからmod_rewriteで消すのだよ」

ということで設置するんですが、Seezooの短縮URL設定が上手くいかないんです。

ちなみに、解決策はすでに@kiyotchiさんがブログに書かれています。素晴らしい!


>> さくらインターネット(スタンダード)にSeezooCMSを入れる。


この通りに書いてももちろんOKですが、自身の勉強のためにもう一度、始めから。
ほとんど上記ブログからの情報です。この場を借りてお礼を。いつも助かってます。

本来ならばこういったことはサーバ設定時にやることかもしれませんが、
OSSという性質で見た場合、どんなサーバーでも動いたほうがいいよね、
アプリケーションで吸収しないといけないところもやっぱあるよね、
という非常に個人的な思いからです。

どうすればうまく設定できるのか?

ポイントは、

  • .htaccessの設定をちゃんとする。
  • PHPCGIモードで動いているかを判定する。
  • CodeIgniterのルーティングをPATH_INFOに基づいて行うようにする。

この3点です。.htaccessは、

引用:
RewriteEngine On
RewriteBase /
RewriteCond $1 !^(index\.php|sitemap.xml|sitemap_ssl.xml|css|js|captcha|uploads|templates|blocks|phpMyAdmin|.+\.gif$|.+\.jpg$|.+\.png$|.+\.js$|.+\.css$|.+\.json$|.+\.ico$|.+\.swf$|.+\.flv$)
RewriteRule ^(.*)$ index.php?__REQ__=$1 [L]


として、RewriteBaseは設定したほうがいいそうです。ふむふむ。
さすがに.htaccessをCIから変更するのは横着すぎるので、これは手動でサーバによって振り分けたほうがよさそうです。
これでリクエストパスが__REQ__というGETパラメータで渡されますね。

で、問題はPHP動作モードの判定と、ルーティングをPATH_INFOから行うことですが、

引用:
次に、/index.phpの冒頭に、次のPHPスクリプトを埋めます。
 

if( isset( $_GET['__REQ__'] ) ){
$_SERVER['PATH_INFO'] = $_GET['__REQ__'];
}

とすればOKみたいです。もちろんこれでOKですが、CIのバージョンアップに追従するため、index.phpは変更しない方法で実装してみたいと思います。
ポイントは、


CodeIgniterのルーティングが始まる前に、$config['uri_protocol']の値が変更されていれば良い

ということで、実際にRouter.phpをフックしてもいいのですが、CIの起動プロセスでは、先にURIクラスがロードされるので、こっちで正規化しておいた方が
確実だと思います。というわけで、URIクラスを拡張して、MY_URI.phpを作成します。以下が拡張したコードです。

system/application/MY_URI.php


class MY_URI extends CI_URI
{
var $_is_cgi = FALSE;

// コンストラクタでフックする
function SZ_URI()
{
parent::CI_URI();

// PHPの動作モードチェック
if ( strpos(PHP_SAPI, 'cgi') !== FALSE )
{
$this->_is_cgi = TRUE;

if (isset($_GET['__REQ__']))
{
// __REQ__というパラメータがあれば、PATH_INFOに格納する
$_SERVER['PATH_INFO'] = $_GET['__REQ__'];
// configの設定値変更
$this->config->set_item('uri_protocol', 'PATH_INFO');
}
}
}

// PHPCGIモードで動いているかを返すメンバ関数も追加
function is_cgi_mode()
{
return $this->_is_cgi;
}
}


Router.phpの内部ではURIクラスのプロパティを元にルーティングするので、コンストラクタの時点でパラメータをセットしておきます。
これでCIのコアクラスに影響を与えずに半自動でルーティングを成功させるようにしました。簡単ですね〜。

is_cgi_mode()メソッドは、アプリ中でCGIで動いているかを取得するのに追加しただけです。

この設定で実際にさくらのスタンダードで動かしたところ、見事index.phpが消えました!
@kiyotchiさん、ありがとうございます!

感想とか


最初は、gethostbyaddr($_SERVER['SERVER_ADDR'])でサーバー名を取得して、sakura.ne.jpを検索する、という
ものすごいハードコーディングをしてました。お恥ずかしい限りです。

PHP_SAPIっていう定義済み変数の存在を知るまでTwitterで小一時間ほど協議してました><全く知りませんでした・・・。
他にも、


ini_get('cgi.force_redirect')

の結果で判定するのも良さそうです。私は定数+strposの方が速かったので、そっちを使いました。
でも.htaceessはやっぱり各自で設定しないといけないので、半自動、という。


こんなちょっとした問題なのに、色々ご意見や手法を教えてくださった@kiyotchiさんを始め、
@bossatamaさん、@riatwさん、ありがとうございます!

2011/01/18追記

id:Kenji_sさんからコメント・トラバを頂きました。さくらインターネットmod_rewrite設定をそのままモジュールで使うのが楽ですね。また、CGIモードかどうかを判定するのはURIクラスではなくConfigクラスの方が適切だと私も思いました。ありがとうございます〜。

※SeezooもConfigクラスを拡張するように変更します^^;