class_implementsという関数があります。これは与えられたクラス名が見たいしているインターフェースを返す関数です。
PHP: class_implements – Manual
<?php interface foo { } class bar implements foo {} print_r(class_implements(new bar)); // PHP 5.1.0 以降、パラメータを文字列として指定しても良い print_r(class_implements('bar')); function __autoload($class_name) { require_once $class_name . '.php'; } // 'not_loaded' クラスをロードするために __autoload を使用する print_r(class_implements('not_loaded', true));
上記コードならば
Array ( [foo] => foo ) Array ( [interface_of_not_loaded] => interface_of_not_loaded )
となります。よくあるhoge(Fuga::classs)な使い方です。
これとクラスを総なめする方法があると、ある範囲に置いてあるファイルに記述されたクラスの内、インターフェースを満たしているもののみを得ることができます(ファイルとネームスペースの一致するPSR-4規約前提)。例えばLaravelで/app/Models以下に置いてあるEloqeuentを探すなら次です。
$classNames = []; foreach (glob(app_path('Models').'\*') as $filename) { if (is_dir($filename)) { continue; } $classNames[] = str_replace([app_path('Models\\'), '.php'], ['App\Models\\', ''], $filename); }
これで特定のフォルダ以下にあるクラスの名前を持つ変数$classNamesが得られます。これとclass_implementsと配列中に特定のインターフェース名があるか調べるコードを組み合わせると
$classNames = []; foreach (glob(app_path('Models\Eloquents').'\*') as $filename) { if (is_dir($filename)) { continue; } $classNames[] = str_replace([app_path('Models\Eloquents\\'), '.php'], ['App\Models\Eloquents\\', ''], $filename); } $implementsClassNames = []; foreach ($classNames as $className) { // この'ArrayAccess'部でインターフェース指定 if (in_array('ArrayAccess', class_implements($className), true)) { $implementsClassNames[] = $className; } }
これで特定のインターフェースを満たした特定のディレクトリ以下にあるクラス名のみを集めた配列$implementsClassNamesが得られました。
このコードが最も活躍するのはObserverパターンです。特定のインターフェースを満たしているクラスこそが監視対象であり、監視対象に対する動作はインターフェースを介して行う、と定義できます。何か増えるたびに一見関係なさそうなオブザーバ処理部を気にする必要がなくなります。
マンガでわかる Observer – Qiita
【Laravel】EloquentモデルのObserverの活用 – 株式会社シーポイントラボ | 浜松のシステム・RTK-GNSS開発