PHPでURIパラメータを照合したメソッドの呼び出しを、環境によって変動するシステムルートに対応させる
独自のフレームワークを開発する際に、URIパラメータよりMVC分離における各メソッドを呼び出す処理に頭をひねったときのメモ。
システムを設置するディレクトリが導入環境により不定である(ドメイン直下やサブディレクトリのどこに配置するかが場合によって変動する)場合に、URIパラメータを照合したメソッドの呼び出しを割り出すのに非常に有効だと思ったので掲載することにした。
今回はわからない人にはわからなく、わかる人にはわかるようなそんな記事内容。
そもそも今回のコードが必要になった経緯については、ざっくり説明するとPHPのフレームワークを作るに当たって、以前触っていたRailsに似た形のController形式を取りたかったからだ。
すなわち、Railsでいうところでroute.rbに書く
// URLパラメータ => '呼び出す(クラス)メソッド' match 'users/login' => 'users#login'
を、今回のPHPで作る独自のフレームワークでは
// URLパラメータ => '呼び出すメソッド' array( 'users/login' => 'users_login' )
といった様式で取りたかったことによる。(実際のフレームワーク側は他の要素との兼ね合いでもっと複雑に処理しているが…)
で、そうしたときに、「users/login」のURIが呼び出されたときに配列キーを照合して、例えばControllerの「users_login」メソッドを呼び出さなければならないわけだが、そこで問題となるのがシステムを設置するディレクトリの導入環境により$_SERVER[‘REQUEST_URI’]が変わってくるということ。
例えば、「example.net」と「example.net/dir/」といった両方のシステム設置ルートが考えられる場合、ドメイン直下である前者は「example.net/users/login」となるし、サブディレクトリである後者は「example.net/dir/users/login」となる。
このとき、単純に$_SERVER[‘REQUEST_URI’]で照合したとして、件の前者は「users/login」となるし、後者は「dir/users/login」がメソッドを呼び出すための照合の条件となってしまう。
フレームワークとシステムを組むに当たって、これは極めて重大な問題だったりする。
常にドメイン直下に配置したり、打ち合わせの段階でドメインを決定するホームページならそのあたりのところは割とフランクで「じゃあ、絶対にこのルートからホームページのパスは動かさない!」と決められるものの、システム・・・引いてはパッケージ型のシステムとなるとそうも行かない。
「こっちの環境ではこうだし、あっちの環境ではこうなる」を常に考慮してものを作っていかなければならない。
最悪、スタンドアローンなクライアントサーバーの環境に導入することもあるわけだし。
ホームページ的な観点であれば、
- array( ‘dir/user/login’ ) => ‘users_login’ );で設定しました!
- 「あ、やっぱりドメイン直下で設置してね^^」
- ‘user/login’に書き換え
- 「ごめん、やっぱりドメイン/dirだわ^^;」
- ‘dir/user/login’に書き換え
という状況も多々で、これで最終的には通る。こういう原始的かつバカげたことが普通にまかり通っている世界だからこそだけど・・・。
今からホームページをやろうとしている人は考え直したほうが良い。世間で制度が色々変わっていく中で、この業界は唯一出来たときから滅私奉公の理念を伝統に受け継ぐ実に素晴らしい仕事だ。嘘だと思って一度この業界に入ってしまえば、高慢な腐れコーダーからの御高説を垂れ流されることはこの先にないだろう。(cattlemuteはニヤリと笑った)
なんか愚痴が入ってしまったので本題に戻ることにする。
それで、問題のドメイン直下でもサブディレクトリでもメソッドとURIリクエストの照合をどのように安定させて処理するか?
ドメイン直下の「/」、サブドメインに設置した際に上記の例でいうところの「/dir」を、どうすれば共通させて自然にかつ円滑に処理できるか?
また、細かい部分に踏み入ると、URIパラメータに$_GETで取得できるパラメータの存在も蔑ろにできない。
冒頭にもあるように、これは非常に頭をひねらせたところであるが、足りない自分の頭をフル回転させてこのコードにたどり着いた。
// システムルートに設置する設定ファイル側 CONST SYSDIR = __FILE__; // レンダリング処理を行うファイル側 private function req_uri_to_method(){ // SYSDIRには設定ファイルのパスが入る。絶対的なドメインのパスである$_SERVER['DOCUMENT_ROOT']でこれを置き換えて、サブディレクトリの名前を割り出す。 // 以下では$_uriにはドメイン直下であれば「空」、サブディレクトリであれば「dir/」が入る $_uri = str_replace( $_SERVER['DOCUMENT_ROOT'] '', SYSDIR . '/' ); // 現在リクエストされているURIパラメータを、サブディレクトリの割り出しで判明した名前と置き換える。 // 以下では、「user/login」がブラウザでリクエストされた際に$_uriにはドメイン直下でもサブディレクトリでも'user/login'が入る。 $_uri = str_replace( $_uri, '', $_SERVER['REQUEST_URI'] ); // $_SERVER['REQUEST_URI']の行頭の「/」が残るためこれを置き換える。また、ついでにURIについている$_GETの文字列も除去する $_uri = preg_replace( '~^/|\?.+?$|\?*$~', '', $_uri ); retuen $_uri; } // レンダリング処理を行うファイル側のControllerを処理させるメソッド側に記載(条件などはもちろんつけるが以下では割愛している) $class = 'Controller_Base_Class'; $method_name = $this->req_uri_to_method(); $class->$_method();
システムルートに設定ファイルを設置して、それをベースに照合することで合理的に処理できるはず。(と思ってる…)
実際にシステムルートに設置する設定ファイルはデータベースの設定なども組み込むため、ファイルそのものにも意味があるので無駄にならない。
コメントアウトにも書いてあるとおり、実際にはフレームワークの構造を考えてもう少し色々やっているが、おおよそのメカニズムについてはこんなところ。
問題の打開を行って揚々としているような記事ではあるが、似たよーなことをしようとしている極々少数派の人のための参考になればいいな~とか思って掲載した。(ちなみにこのコードそのものを作ったのは実に半年弱前である。その後もちょこちょこ手を加えたり修正したりはしたけど。)
で、先週頭くらいに前々から作っていたPHPのフレームワークがようやく完成した。
こいつを作るに当たって、開発の際にコーディング面で詰まったところについては、また追々掲載していきたいと思う。
・・・それにしても、システムログインだとか、フロントエンドとバックエンドとでデータのやり取りを行う画面だとかの、作っていて一番楽しい部分にやっと入ることが出来た。
こういう、論理を辿ってものづくりをしていくのって、大好き。
ホームページでやっているような画面に表示するアニメーションやエフェクトがどうとか、文字や画像のサイズや配置がどうとか、見る側の感覚によって左右される実に不安定かつ非論理的なものを触るのが苦痛となった今なら言えることだけど。
そういえば、最近NGINXが何処かの企業に買収されたとかなんとか。
少し前にGithubもMicrosoftに買収されたとかそういう話もあったし、相当前にOracleがMySQLを買収してしたとかの話もあったし・・・。
そのうち、WordPressとかも買収される時代がくることもあるんだろうか。
やっぱり、ごくごくわずかでも言語ベースで触れるのであれば、フレームワークないしスケールモデルくらいは独自で持っておくべきなのかもしれないね。