symfonyでURLを.htaccessでRewriteする方法
私の週末は十数行程度のコード修正と数え切れないsymfonyソースコードのCtrl+Z(元に戻す)に集約される。。。
symfonyのチュートリアルでは開発環境がどうであれ
http://askeet/
のようにプロジェクト名をURLにしてアクセスするように進められているが、まず、他のシステムを動かすためにわざわざhttpd.confとhostを書き換えてApacheを再起動するのは面倒だし、前回の文字化け投稿と同様、納品先のことも考慮したらhttpd.confでの設定が出来ないことも考慮する必要が出てくる。
と言うわけでhttpd.confを使わずに
http://localhost/symfony/projectname/
で表示させる方法。
まず、
http://localhost/symfony/projectname/web/
これはすぐに出来る。つーか、彼らの http://askeet/ にする設定を外せばいいだけ。
只、そうするとsymfonyが提供するお洒落なJava Scriptタブやエラー画面が表示されなくなる。どうでもいい魚の骨。
httpd.confでの設定ではPEARディレクトリへの参照パスは許されているが、.htaccess では権限エラーになってしまう。Windowsでそこの設定を変更するのを調べるのは面倒なので、htdocs配下に
PEAR\data\symfony\web\sf
の中身全部をコピー(突っ込み歓迎)。ちなみに私は htdocs/symfony/ 配下にコピー。
symfony/projectname/web/.htaccess を修正。
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^sf/(.*)$ /symfony/sf/$1 [L]
を #RewriteBase のすぐ下あたりに追加。
へい、mod_rewriteです。簡単に説明すると
RewriteCond %{REQUEST_FILENAME} !-d
はリクエストファイルの文字列がディレクトリでは無い場合
RewriteCond %{REQUEST_FILENAME} !-f
はリクエストファイルの文字列がファイルでは無い場合
つまり、そこに存在しない場合(と私は解釈@突っ込み歓迎)
その存在しないリクエストファイルの文字列が「sf/」で始まり、任意の1文字が0回以上ある集合体で終わる場合(カッコで囲むので$1と言う変数で認識してくれる)
/symfony/sf/
の後ろに$1を付け足し
そいつらの在りかをそこで探しておくれ
で、[L]はこのリクエストファイルの文字列でのrewriteはここで終了、と言う意味。
以後のmod_rewriteの説明は割愛。
魚の骨が取れることを確認。
次に
http://localhost/symfony/projectname/web/
ではなく、
http://localhost/symfony/projectname/
で表示できるようにがんばることにする。そこからまた苦労が始まる。。。
もちろん、webの中身を一個上のフォルダに移動すればそれだけで完了だ。しかしそこには生きる上での喜びがない(私の場合)。
と言うわけで、まずは
symfony/projectname/
に
.htaccess
を作成。つーか、私はsymfonyさんのをコピーした。理由:共通性を図るため。理由:手間を省くため。
で、
Options +FollowSymLinks +ExecCGI
RewriteEngine On
#RewriteBase /
以外は全部消す。下のErrorDocumentは消しても残してもどちらでも良い。
そこで凄いことに気付く。
http://localhost/symfony/projectname/
としたら
ディレクトリ構造丸見えじゃん!
いかんざき!
つーわけで
Options +FollowSymLinks +ExecCGI
の下に
Options -Indexes
を追加。見んなよ、と言う意味。
RewriteBaseのコメントを外し
RewriteBase /symfony/projectname/web/
とする。書き換える時はこれを頭につけておくれ、と言う意味。
その下に
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)\.php/(.*)$ $1.php/$2 [QSA,L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,L]
と追加。4行目はファイル名を指定していない時( 例:http://hogehoge.nk/ )に必要。
これで
http://localhost/symfony/projectname/
で表示されるようになった。
のに・・・。
困ったことに。。。
リンク先が
http://localhost/symfony/projectname/web/
になっている。。。
普通にHTMLで組んだら
http://localhost/symfony/projectname/
なのに
link_to関数を使うとどうしても'web'がついてしまう。
そして私は更なるsymfonyソースコードの森の奥へ、長い放浪の旅に出ることになる。。。
解決案。
symfony/projectname/apps/appname/lib/
に
myFrontWebController.class.php
と言うクラスを生成。
PEAR\symfony\controller\sfWebController.class.php
の
genUrl関数をこちらのクラスでオーバーライドする(sfFrontWebController.class.phpはsfWebController.class.phpを継承している)。
最初に
$url = parent::genUrl($parameters, $absolute);
として親の関数を実行してから追加処理を加える。下記ソース。牡蠣ソース。
class myFrontWebController extends sfFrontWebController
{
public function genUrl($parameters = array(), $absolute = false)
{
$url = parent::genUrl($parameters, $absolute);
//切り取りたい文字列
$cut_str = 'web/';
if((preg_match('/\.php/', $url) ||
preg_match('/index\.php/', $_SERVER['SCRIPT_NAME'])) &&
(!preg_match('/^http/', $url) ||
preg_match('/'.$_SERVER['SERVER_NAME'].'/', $url)))
$url = substr($url, 0, strpos($url, $cut_str)).
substr($url, strpos($url, $cut_str) + strlen($cut_str));
return $url;
}
}
if文の理由はCSSやJavaScriptファイルを'web'配下を参照のままで良いため。
で、
symfony/projectname/apps/appname/config/factories.yml
を修正。
all:
controller:
class: myFrontWebController
13行目から3行分コメントアウトし、「sf」のところを「my」に変更。
以上。
コンソールで
symfony cc
を忘れないように。
表示確認。画面遷移確認。データ修正処理確認。
パチパチパチ~~。
今のところアプリ単位やらなければならないようなのでそこはちと手間。
後、ホントは
symfony/projectname/apps/appname/lib/myWebRequest.class.php
にし
class myWebRequest extends sfWebRequest
{
public function getScriptName()
{
$pathArray = $this->getPathInfoArray();
$scriptName = $pathArray['SCRIPT_NAME'];
//切り取りたい文字列
$cut_str = 'web/';
if(preg_match('/\.php/', $scriptName))
$scriptName = substr($scriptName, 0, strpos($scriptName, $cut_str)).
substr($scriptName, strpos($scriptName, $cut_str) + strlen($cut_str));
return $scriptName;
}
}
とした方がカッコ良かったんだが、何故だ!CSSやJSの連中もこの影響を受ける。な~ぜ~~
後、当初は
symfony/projectname/apps/appname/config/config.php
の終わりに
sfConfig::set('sf_web_dir_name', '');
sfConfig::set('sf_web_dir', sfConfig::get('sf_root_dir'));
sfConfig::set('sf_upload_dir', sfConfig::get('sf_root_dir').DIRECTORY_SEPARATOR.sfConfig::get('sf_data_dir_name'));
で済むと思っていたのだが、どうやらこいつら定数は・・、ゴミ??変更しても全く影響ないので消すことにしよう。
さて、ここまで読んだ人は果たしているのだろうか。ここまで読んでくれた方にとってもいいネタを提供しよう。
debug_backtrace()
と言うPHP関数がある。
出来ればもっと昔から知っておきたかった。
この関数を使って
print_r(debug_backtrace());
とすればそのポイントでの関数の呼び出し元を全部吐き出してくれる。.NETでのスーパー機能をPHPでも再現できることを知った私はもはや喜び組。
ちなみに似たような関数、Rubyにもあるのかな??
最後に、PEARにsymfonyが入っていないレンタルサーバーで動かすには
開発環境のPEAR内のsymfonyグッズを
symfony/projectname/lib
内に突っ込んで
symfony/projectname/config/config.php
の
// symfony directories
$sf_symfony_lib_dir = '/hoge/php/pear/symfony';
$sf_symfony_data_dir = '/hoge/php/pear/data/symfony';
を修正するだけ、・・・だと思う。
納品はまだ全然先なのでその点の検証はしばらく保留することに。
広ブロっち♪ d(^-^)