CodeIgniterの学習 50 - HMVCモジュール拡張のインストールと改造をする( HMVCを使ってブログパーツチックにコントローラやビュー内で別コントローラ(とそのビュー)を呼べるようにするその2)
今日は昨日のエントリーの続き。CodeIgniterのHMVC拡張をおこなう。(PHP5用)
ついでに一部不満な箇所を微改造。(モデル等のファイル名の大文字小文字を区別するところ等)
(今日のエントリーはインストールと微改造まで、続きは明日)
インストール
設置するソースは3ファイル手順1)
元ネタの Modular Extensions - HMVC からソースをダウンロードする。
http://codeigniter.com/wiki/Modular_Extensions_-_HMVC/ の
Download new version 5.1: File:modular extensions 5140.zip (PHP5 only)
からダウンロードできる。
手順2)
解凍したファイルの
を、application/libraries に設置する。
インストールは以上。
これで既存のコントローラの設置場所以外に、
application/modules/モジュール名/controllers/モジュール名.php
のコントローラが
http://example.com/モジュール名/(メソッド名)/
で呼べるようになる。
機能毎にソース分割できるね。
またコントローラの中で別のコントローラを呼び出すことも出来るようになる。
モジュール内のソース体系の流儀は、CodeIgniter標準と同様に
application/modules/モジュール名/libraries/
application/modules/モジュール名/models/
application/modules/モジュール名/views/
application/modules/モジュール名/language/
みたいにモジュール内専用の ライブラリ、モデル、ビュー、言語ファイル
を任意で設置出来る。(libraries/が不要ならディレクトリは存在しなくて良い)
不満な箇所の微改造その1、読み込むファイル名先頭の大文字小文字を同一として扱う
現状バージョンのHMVC拡張だと、
モデル等の物理ファイル名の先頭の大文字小文字と、
CIにロードする時の引数の大文字小文字がちぐはぐな時に
読み込み出来なくてエラーになったりする。
ちなみに、CodeIgniter 1.7標準のローダー(system/libraries/Loader.php)
では、ファイル名先頭の大文字小文字は区別しないようになっている。
// We test for both uppercase and lowercase, for servers that
// are case-sensitive with regard to file names
ていうのがCI標準のローダー内の_ci_init_class() 等に見受けられるのだが、
インストールしたHMVC拡張のソースのローダー部分(application/libraries/Modules.php)
にはその処理が入っていない。
このせいで、既存のソース(例えばFreak_Auth等)を使っていると、ロード時にエラーになって動作しなくて困るので、
CodeIgniter 1.7標準のローダーに習い、以下の箇所を改造してみた。
(08/12/02 改造箇所追加、変更により記述書き換え)
修正ファイル1:application/libraries/Modules.php (modular extensions 5140)
(90行目付近から抜粋)
<?php //上略 /** Library base class autoload **/ public static function autoload($class) { /* don't autoload CI_ or MY_ prefixed classes */ if (strstr($class, 'CI_') OR strstr($class, 'MY_')) return; //ファイル名先頭の大文字込み時を区別しないようにした //if(is_file($location = APPPATH.'libraries/'.$class.EXT)) // include_once $location; $file = str_replace(EXT, '', $class); $location = APPPATH.'libraries/'.$file.EXT; if(is_file($location)){ include_once $location; }elseif ( file_exists( APPPATH . 'libraries/' . strtolower( $file ) . EXT ) ) { include_once APPPATH . 'libraries/' . strtolower( $file ) . EXT; }elseif ( file_exists( APPPATH . 'libraries/'. ucfirst( strtolower( $file ) ) . EXT ) ) { include_once APPPATH . 'libraries/'. ucfirst(strtolower( $file ) ) . EXT; }else{ //show_error( "{$location} does not found" ); } //ファイル名先頭の大文字込み時を区別しないようにした end } /** Load a module file **/ public static function load_file($file, $path, $type = 'other', $result = TRUE) { $file = str_replace(EXT, '', $file); $location = realpath($path.$file.EXT); if ($type === 'other') { if (class_exists($file, FALSE)) { log_message('debug', "File already loaded: {$location}"); return $result; } //ファイル名先頭の大文字込み時を区別しないようにした //include_once $location; if ( file_exists( $path . strtolower( $file ) . EXT ) ) { include_once $path . strtolower( $file ) . EXT; }elseif ( file_exists( $path . ucfirst( strtolower( $file ) ) . EXT ) ) { include_once $path . ucfirst( strtolower( $file ) ) . EXT; }else { show_error( "{$path}{$file}" . EXT . " does not found" ); } //ファイル名先頭の大文字込み時を区別しないようにした end } else /* load config or language arrays */ { //ファイル名先頭の大文字込み時を区別しないようにした include $location; if ( ! isset($$type) OR ! is_array($$type)) show_error("{$location} does not contain a valid {$type} array"); $result = $$type; /* if ( file_exists( $path . strtolower( $file ) . EXT ) ) { include $path . strtolower( $file ) . EXT; }elseif ( file_exists( $path . ucfirst( strtolower( $file ) ) . EXT ) ) { include $path . ucfirst( strtolower( $file ) ) . EXT; }else { show_error( "{$path}{$file}" . EXT . " does not found" ); } $result = $$type; */ //ファイル名先頭の大文字込み時を区別しないようにした end } log_message('debug', "File loaded: {$location}"); return $result; } /** * Find a file * Scans for files located within application/modules directory. * Also scans application directories for models and views. * Generates fatal error on file not found. **/ public static function find($file, $path, $base, $subpath = NULL) { $search = array(); /* override subpath when loading from a subdirectory */ if ($pos = strrpos($file, '/')) { $subpath = substr($file, 0, $pos); $file = substr($file, $pos + 1); } ($path) AND $path .= '/'; ($subpath) AND $subpath .= '/'; $file_ext = strpos($file, '.') ? $file : $file.EXT; if ($path) /* ensure we have a module path */ { ($subpath) AND $search[] = MODBASE.$path.$base.$subpath; $search[] = MODBASE.$path.$base; } switch ($base) { case ('libraries/'): $file_ext = ucfirst($file_ext); break; case ('views/'): case ('models/'): ($subpath) AND $search[] = APPPATH.$base.$subpath; $search[] = APPPATH.$base; ($path) AND $search[] = APPPATH.$base.$path; } foreach ($search as $path2) { //ファイル名先頭の大文字込み時を区別しないようにした //if (is_file($path2.$file_ext)) return array($path2, $file); if(is_file($path2.$file_ext)){ return array($path2, $file); }elseif ( is_file( $path2 . strtolower( $file_ext ) ) ) { return array( $path2, strtolower( $file ) ); }else { if ( is_file( $path2 . ucfirst( strtolower( $file_ext ) ) ) ) { return array( $path2, ucfirst( strtolower( $file ) ) ); } } //ファイル名先頭の大文字込み時を区別しないようにした end } /* file not found. show an error for these types */ if ($base == 'views/' OR $base == 'models/') show_error("Unable to locate the file: {$file_ext} in: {$base}"); /* handle the rest back in the controller */ return array(FALSE, $file); } //下略 ?>
(変更点は日本語コメントで挟み込んでいる間の箇所)
修正ファイル2:application/libraries/Controller.php(modular extensions 5140)
(325行目付近から抜粋)
<?php //上略 public function model($model, $object_name = NULL, $connect = FALSE) { $ci = modules::$registry[$this->_class]; //モデル名は勝手に小文字には変えない //($_alias = $object_name) OR $_alias = strtolower(end(explode('/', $model))); ($_alias = $object_name) OR $_alias = end(explode('/', $model)); //モデル名は勝手に小文字には変えないend if (isset(self::$models[$_alias])) return $ci->$_alias; list($path, $model) = modules::find($model, $this->_module, 'models/'); (class_exists('Model', FALSE)) OR load_class('Model', FALSE); if ($connect !== FALSE) { if ($connect === TRUE) $connect = ''; $this->database($connect, FALSE, TRUE); } modules::load_file($model, $path); $model = ucfirst($model); $ci->$_alias = new $model(); self::$models[$_alias] = $ci->$_alias; $ci->$_alias->_assign_libraries(); return $ci->$_alias; } //下略 ?>
(変更点は日本語コメントで挟み込んでいる間の箇所)
これで既存のソースは変えなくても(ほぼ)HMVCは動作するようになった。
(08/12/02 追記 うーん(Freak_Authとはいろいろと相性悪いな。まあ改造するから良いけど))
次はHMVC拡張を使って、テストモジュールのサンプルを作って動作確認してみる。+ついでにURLを直接叩かれたくないモジュールのルーティングの調整を行う。
記録し始めてなんだかんだで2ヶ月ちょっと経ったな。一応実務でも実験的に使っている。
昔のエントリーでタスクリストを作るとか書いたけど、放置状態だな。ま、いっか。