効率的な PHP の自動ロードと命名戦略
-
16-09-2019 - |
質問
最近のほとんどの Web 開発者と同様に、私も Web アプリとサイトに対する堅牢な MVC アーキテクチャの利点を十分に楽しんでいます。PHP で MVC を実行する場合、オートロードは明らかに非常に便利です。
のファンになりました spl_autoload_register
単に単一のものを定義するだけではなく、 __autoload()
それぞれ独自の自動ロードを使用するさまざまな基本モジュールを組み込んでいる場合、これは明らかに柔軟性が高いためです。しかし、私は自分が作成した読み込み関数について素晴らしいと感じたことはありません。ロード可能なクラスを探すために、多くの文字列チェックとディレクトリ スキャンが必要になります。
たとえば、ベースパスが次のように定義されているアプリがあるとします。 PATH_APP
, 、という名前のディレクトリを含む単純な構造 models
, views
そして controllers
. 。私はファイルに名前を付けるという命名構造をよく採用します。 IndexView.php
そして IndexController.php
適切なディレクトリ内にあり、モデルには通常、デフォルトでは特定のスキームはありません。この構造には、次のように登録されるローダー関数があるかもしれません。 spl_autoload_register
:
public function MVCLoader($class)
{
if (file_exists(PATH_APP.'/models/'.$class.'.php')) {
require_once(PATH_APP.'/models/'.$class.'.php');
return true;
}
else if (strpos($class,'View') !== false) {
if (file_exists(PATH_APP.'/views/'.$class.'.php')) {
require_once(PATH_APP.'/views/'.$class.'.php');
return true;
}
}
else if (strpos($class,'Controller') !== false) {
if (file_exists(PATH_APP.'/controllers/'.$class.'.php')) {
require_once(PATH_APP.'/controllers/'.$class.'.php');
return true;
}
}
return false;
}
その後も見つからない場合は、models ディレクトリ内のサブディレクトリをスキャンする別の機能を使用する可能性があります。ただし、if/else 処理、文字列チェック、ディレクトリ スキャンはすべて非効率であるように思えるので、改善したいと考えています。
他の開発者がどのようなファイル命名と自動ロード戦略を採用するのか非常に興味があります。私は特に、自動ロードの代替手段ではなく、効率的な自動ロードに採用できる優れた手法を探しています。
解決
これは私がすべてのプロジェクトで使用しているものです(最後のプロジェクトのソースから直接抜粋したものです)。
public static function loadClass($class)
{
$files = array(
$class . '.php',
str_replace('_', '/', $class) . '.php',
);
foreach (explode(PATH_SEPARATOR, ini_get('include_path')) as $base_path)
{
foreach ($files as $file)
{
$path = "$base_path/$file";
if (file_exists($path) && is_readable($path))
{
include_once $path;
return;
}
}
}
}
SomeClass_SeperatedWith_Underscores を検索すると、現在のインクルード パスの各ディレクトリをルートとする SomeClass_SeperatedWith_Underscores.php に続いて SomeClass/SeperatedWith/Underscores.php が検索されます。
編集: 必ずしも処理時間のためではなく、開発の効率化のためにこれを使用していることを公表したかっただけです。パス上に PEAR がある場合、これを使用すると、クラスを使用するだけで済み、必要なときにクラスをインクルードする必要がなくなります。
私はクラスをディレクトリの階層内に保持する傾向があり、名前空間はアンダースコアで分割されています...このコードを使用すると、必要に応じてファイル構造をきれいに整頓した状態に保つことができます。また、必要に応じて、ネストされたディレクトリのない簡単なクラス ファイルを挿入することもできます (被告であるライブラリに 1 つまたは 2 つのクラスを追加しますが、ライブラリの一部ではありません)。現在取り組んでいるプロジェクトです。)
他のヒント
私は、このソリューションに上陸します:
私は(別のモジュール/システム用のサブフォルダが含まれています)私のクラスライブラリフォルダを横断し、クラス定義を探しているファイルの内容を解析し、単一のスクリプトを作成しました。それはPHPファイル(非常に単純な正規表現パターン)でのクラス定義を見つけた場合、それは、シンボリックリンクを作成します:
class_name.php -> actual/source/file.php
これは私が唯一のクラス名とメインのシンボリックリンクフォルダへのパスを必要とし、任意のパス/文字列操作を行う必要はありません1つの単純なオートロード機能を使用することができます。
最良の部分は、私は完全に私のソースコードを再配置したり、新しいサブシステムを追加し、ちょうどすべてが自動的にロードするために持っているリンク生成スクリプトを実行することができるということです。
あなたがすべてでオートロード機能を使用してはいけません。オートロード機能は怠けているためです。あなたがそれらを含めるときは、自分の含まれたファイルへの明示的なパスを提供する必要があります。あなたのオートロード機能は、これらのファイルを見つけることができるなら、あなたは明示的にそれらを見つけるためのコードがあります。あなたは、コードの表示部に取り組んでオートロード機能は、それを処理させることで、新しいビュークラスをロードしようとしているとき、それは最初のクラスがモデルクラスであると仮定し?それは非効率的です。代わりにあなたのコードだけである必要があります:
include_once $this->views_path . $class . '.php';
あなたは、複数の「ビュー」のパスが必要な場合は、ビューをロードする機能を作ります:
public function load_view($class) {
// perhaps there's a mapping here instead....
foreach ($this->views_paths as $path) {
$filename = $path . $class . '.php';
if (file_exists($filename)) {
include_once $filename;
}
}
throw ....
}
いずれにせよ、インクルードが発生含ま時点で、あなたがロードしたいクラスについての最大の/最も正確な情報を持っています。完全にクラスをロードするためにその情報を使用することでのみ、効率的なクラスローディング戦略です。はい、あなたはより多くのクラス変数または(禁止天国)いくつかのグローバル変数で終わることがあります。しかし、それは自分のクラスのファイルシステムの怠惰とスキャン部分であることよりも良いのトレードオフである。