PHP では使うクラスを予め宣言する、というルールがあります。
これを忘れていると、実行時にエラーが表示されます。
Fatal error: Uncaught Error: Class 'XXXXXX' not found in /Sample/Test.php:26 Stack trace: #0 {main} thrown in /Sample/Test.php on line 26
C# などを経験してると、これは非常にストレスです。(C# では宣言をする必要性も少なく、右クリックで using を自動的に挟めるなど便利な環境が揃っているため)
また、このルールに慣れ親しんだ人でも、
- 大規模な開発案件で、1ソースに数十の require が並ぶ
- テストログ吐くためだけに require しないといけない
- 後で使わなくなったクラスの require がどれだかわからない -> 知らない間に実行コスト増加
このように色々としんどい要素が多い。このしんどさを解消しましょう。
ソースコード
autoLoader.php
<?php class autoLoader { protected $dirs; function __construct($dir = null) { if ($dir != null) { $this->dirs[] = $dir; } } public function Register() { spl_autoload_register(array($this, 'autoLoad')); } public function AddDirectory($dir) { $this->dirs[] = $dir; } private function autoLoad($className) { foreach ($this->dirs as $dir) { $file = $dir . '/' . $className . '.php'; if (is_readable($file)) { require $file; return; } } } } ?>
テスト
testClass.php
<?php class testClass { public static void exec() { echo 'exec class</br>'; } } ?>
sample.php
トップコードに autoLoader を入れておくと、以後呼び出されるクラスファイルは全て require(_once) が必要なくなります。
<?php require("autoLoader.php"); // sample.php と同じパスに、全ての php があるとする $autoLoader = new autoLoader(dirname(__FILE__)); // 他にファイルを探すディレクトリがある場合、AddDirectory で追加する // $autoLoader->AddDirectory("...."); $autoLoader->Register(); // require がなくても動く testClass::exec(); ?>
解説
spl_autoload_register によって「インスタンス生成時、require されていないクラスであれば呼ばれる関数」を定義します(autoLoad)。
autoLoad では、読み込まれていないファイルパスを自動生成し、require します。
(構造的に、検索するディレクトリが多いほど実行時間がかかります)
注意点
ファイル名=クラス名にしておく必要があります。ゆえにこういうのはダメです。
ファイル名:test_sample.cs、クラス名:TestSample
メソッド autoLoad を改造すればこういうパターンも有りにすることはできますが、実行コストが上がってしまうのでちょっと…。
実行速度
さすがに require を直接書くより単純速度は遅くなります。(サーバーのファイル検索速度に依存するでしょう)
とはいえ、無駄なファイルを require せずに済むロス回避の面もありますし、例えばこんな感じで動的にクラスを呼んでる場合、require を1つで済ますことができ、かえって速度上昇するケースもあるかもしれません。
// テーブルは50テーブルくらいある。require であれば 50 記述する必要がある
call_user_func($tablename. '::GetRecordCount', $search_id)
総じて、余程 require が絶大なパフォーマンスを誇るケースでなければ、こちらの方がいいんじゃないかと思います。