しかし、get_mime_by_extension(‘hoge.jpg’)とするとPHP Warningが出たので、system/helper/file_helper.phpにトレースを入れてテストしてみたところ、 application/config/mimes.phpをロードできていない様子。
そこでこのようなチケットを発見。
https://bitbucket.org/ellislab/codeigniter/issue/354/get_mime_by_extension-triggers-php-error
Some dirty trick is changing the source Code: require_once -> requirerequire_onceをrequireにすれば動くよ!とのこと。
ちなみにこの関数のソースは次のようになっています。
if ( ! function_exists(‘get_mime_by_extension’)){ function get_mime_by_extension($file){ $extension = strtolower(substr(strrchr($file, ‘.’), 1)); global $mimes; if ( ! is_array($mimes)){ if ( ! require_once(APPPATH.’config/mimes.php’)){ return FALSE; } } if (array_key_exists($extension, $mimes)){ if (is_array($mimes[$extension])){ // Multiple mime types, just give the first one return current($mimes[$extension]); }else{ return $mimes[$extension]; } }else{ return FALSE; } } }
requireとrequire_onceの違いは、ドキュメントをさらっと読むだけでは、それぞれ「毎回読み込む」「1回しか読み込まない」としか読み取れません。なによりドキュメントがひどい。
require_once()
http://php.net/manual/ja/function.require-once.php
_once の振る舞い、およびそれが _once なし版とどのように異なるのかについての情報は、 include_once() のドキュメントを参照ください。↓
include_once()
http://www.php.net/manual/ja/function.include-once.php
この関数の動作についての情報は include() のドキュメントを参照ください。↓
include()
http://www.php.net/manual/ja/function.include.php
読み込まれるファイルで定義された関数がある場合、 これらは、return()の前後によらず メインファイルで使用できます。 このファイルが二度読み込まれた場合、PHP 5は関数が定義済みであるため 致命的なエラーを発生します。 一方、PHP 4は return()の後に定義された関数については、 エラーを発生しません。 ファイルが読み込み済みであるかどうかを調べ、 読み込まれるファイルの内容を条件分岐で返すかわりに include_once()を使用することを推奨します。えーと…「_once の振る舞い、およびそれが _once なし版とどのように異なるのかについての情報」はどこへ行ってしまったんでしょうか?
_onceの話が出てくるのはこの数行だけなんです。
これでは、「何回かinclude/requireする可能性のある個所では_onceにしとけばいいんだな」としか読み取れないでしょう。
しかし両者の違いは、こんなサンプルコードでも分かるはずです。
hoge.php <?php return “hoge”;
test.php <?php var_dump(require_once((dirname(__FILE__).”/hoge.php”))); var_dump(require_once((dirname(__FILE__).”/hoge.php”))); var_dump(require((dirname(__FILE__).”/hoge.php”)));
test.phpの実行結果は
string(4) “hoge” bool(true) string(4) “hoge”となります。
つまり、2回目以降のrequire_onceは、指定したファイルの中身ではなく、boolean値を返します。
つまりこれは、いくつかあるincludeの挙動のうち、「return記述の無いPHP実行ファイルをincludeした場合の返り値」と同じ挙動になるわけです。
このことについてドキュメントには明記されておらず、しいて言えば、
include()
http://www.php.net/manual/ja/function.include.php
内の「例5 include()とreturn()文」のコード例に示唆されているだけです。
と同時に、
include() は、ファイルを見つけられない場合に warning を発行します。 一方 require() の場合は、同じ場合に fatal error を発行する点が異なります。とあります。さらに、
require()
http://www.php.net/manual/ja/function.require.php
require() は include() とほぼ同じですが、失敗した場合に E_COMPILE_ERROR レベルの致命的なエラーも発生するという点が異なります。 つまり、スクリプトの処理がそこで止まってしまうということです。 一方 include() の場合は、警告 (E_WARNING) を発するもののスクリプトの処理は続行します。ということは、require_onceを単純にIF文に突っ込むだけでは(そもそもこの記述はIDEに怒られると思いますが)判定は成り立たない、ということになります。
CI2.0.2では、この関数は次のように記述されています。
if ( ! function_exists(‘get_mime_by_extension’)){ function get_mime_by_extension($file){ $extension = strtolower(substr(strrchr($file, ‘.’), 1)); global $mimes; if ( ! is_array($mimes)){ if (defined(‘ENVIRONMENT’) AND is_file(APPPATH.’config/’.ENVIRONMENT.’/mimes’.EXT)){ include(APPPATH.’config/’.ENVIRONMENT.’/mimes’.EXT); }elseif (is_file(APPPATH.’config/mimes’.EXT)){ include(APPPATH.’config/mimes’.EXT); } if ( ! is_array($mimes)){ return FALSE; } } if (array_key_exists($extension, $mimes)){ if (is_array($mimes[$extension])){ // Multiple mime types, just give the first one return current($mimes[$extension]); }else{ return $mimes[$extension]; } }else{ return FALSE; } } }多少PATHなどが変わった影響はありますが、ファイル存在判定と読み込み後のデータ存在判定を分離したこと、requireをincludeに変えたことで、処理が停止することが無くなった、という具合に修正されています。
じゃあ2.0.2を使えばいいのね、というわけにもいかないのが悩ましいところで、2.0.2は若干バギーだったため、2.0.3のリリースが急がれているようです。日本語版も2.0.2を飛ばして2.0.3に向かうとのこと。
とりあえずsystem/helper/file_helper.phpを直すか、get_mime_by_extension()を使わないか、という選択肢になりますかね…。