PHP で必要なクラスをインクルードする方法
質問
使用する必要のあるすべてのクラスがスクリプトにアクセスできるようにするために、PHP スクリプトに非常に多くのファイルを「含める」必要があるという問題に対処するためのベスト プラクティスは何だろうと考えています。
現在、私はちょうど使用しています include_once 直接アクセスするクラスを含めます。それらのそれぞれは、 include_once
アクセスするクラス。
の使用を検討しました __autoload
関数ですが、クラスファイルをディレクトリツリーに整理する予定がある場合、hat はうまく機能しないようです。これを行うと、探しているクラスが見つかるまでディレクトリ ツリーをたどることになるようです。 また、これが異なる名前空間にある同じ名前のクラスにどのような影響を与えるのかもわかりません。
これをより簡単に処理する方法はありますか?
それともPHPは単に「」には適していないのでしょうか?企業的な" タイプのアプリケーションでは、多くの異なるオブジェクトがすべて別々のファイルに配置され、さまざまなディレクトリに存在する可能性があります。
解決
私が普段持っているアプリケーション setup.php
すべてのコア クラス (つまり、フレームワークと付属のライブラリ)。私のカスタム クラスは、ディレクトリ レイアウト マップを利用したオートローダーを使用してロードされます。
新しいクラスが追加されるたびに、ディレクトリ ツリー全体をスキャンしてモデル クラスを検索し、クラス名をキー、パスを値として持つ連想配列を構築するコマンド ライン ビルダー スクリプトを実行します。次に、__autoload 関数はその配列内のクラス名を検索し、インクルード パスを取得します。コードは次のとおりです。
autobuild.php
define('MAP', 'var/cache/autoload.map');
error_reporting(E_ALL);
require 'setup.php';
print(buildAutoloaderMap() . " classes mapped\n");
function buildAutoloaderMap() {
$dirs = array('lib', 'view', 'model');
$cache = array();
$n = 0;
foreach ($dirs as $dir) {
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $entry) {
$fn = $entry->getFilename();
if (!preg_match('/\.class\.php$/', $fn))
continue;
$c = str_replace('.class.php', '', $fn);
if (!class_exists($c)) {
$cache[$c] = ($pn = $entry->getPathname());
++$n;
}
}
}
ksort($cache);
file_put_contents(MAP, serialize($cache));
return $n;
}
autoload.php
define('MAP', 'var/cache/autoload.map');
function __autoload($className) {
static $map;
$map or ($map = unserialize(file_get_contents(MAP)));
$fn = array_key_exists($className, $map) ? $map[$className] : null;
if ($fn and file_exists($fn)) {
include $fn;
unset($map[$className]);
}
}
ファイルの命名規則は [class_name].class.php である必要があることに注意してください。クラスが検索されるディレクトリを変更します autobuild.php
. 。クラスが見つからない場合は autoload 関数からオートビルダーを実行することもできますが、プログラムが無限ループに陥る可能性があります。
シリアル化された配列は非常に高速です。
@ジェイソンマイケル:PHP4は死んだ。それを乗り越えてください。
他のヒント
spl_autoload_register を使用して複数のオートロード関数を定義できます。
spl_autoload_register('load_controllers');
spl_autoload_register('load_models');
function load_models($class){
if( !file_exists("models/$class.php") )
return false;
include "models/$class.php";
return true;
}
function load_controllers($class){
if( !file_exists("controllers/$class.php") )
return false;
include "controllers/$class.php";
return true;
}
物理ディレクトリにマップする構造化された命名規則を使用して、クラス ファイルの場所をプログラムで決定することもできます。これが Zend でのやり方です Zend フレームワーク. 。それで、あなたが電話するとき、 Zend_Loader::loadClass("Zend_Db_Table");
アンダースコアで分割してクラス名をディレクトリの配列に展開し、その後 Zend_Loader クラスが必要なファイルをロードします。
すべての Zend モジュールと同様に、ローダーだけを独自のクラスで使用できると思いますが、私は Zend の MVC を使用するサイトの一部としてのみ使用しました。
しかし、あらゆる種類の動的クラスローディングを使用する場合、負荷時のパフォーマンスについて懸念がありました。たとえば、次を参照してください。 このブログ投稿 Zend_Loader とクラス ファイルのハード ロードを比較します。
PHP インクルード パスを検索する必要があるためパフォーマンスが低下するだけでなく、オペコード キャッシュも無効になります。その投稿のコメントから:
任意の動的クラス ローダーを使用する場合、APC は単一のリクエストでどのファイルがロードされるかわからないため、これらのファイルを完全にキャッシュできません。ファイルをハードロードすることにより、APC はファイルを完全にキャッシュできます。
__autoload
クラスがディレクトリ ツリー内のどこにあるかを関数に伝える一貫した命名規則がある場合、これはうまく機能します。MVC は、クラスをモデル、ビュー、コントローラーに簡単に分割できるため、この種のことに特に適しています。
あるいは、クラスのファイルの場所に名前の連想配列を保存しておいて、 __autoload
この配列をクエリします。
これまでの提案のうち、私は Kevin の提案に賛成ですが、それが絶対である必要はありません。__autoload で使用できるいくつかの異なるオプションが表示されます。
- すべてのクラス ファイルを 1 つのディレクトリに置きます。ファイルにクラスにちなんで名前を付けます。つまり、
classes/User.php
またはclasses/User.class.php
. - モデルを 1 つのディレクトリに配置し、コントローラーを別のディレクトリに配置するという Kevin のアイデア。すべてのクラスが MVC フレームワークに適切に適合していればうまく機能しますが、状況が複雑になる場合もあります。
- クラス名にディレクトリを含めます。たとえば、Model_User というクラスは実際には次の場所にあります。
classes/Model/User.php
. 。__autoload 関数は、ファイルを見つけるためにアンダースコアをディレクトリ区切り文字に変換することを認識します。 - ディレクトリ構造全体を一度解析するだけです。__autoload 関数内、またはそれが定義されている同じ PHP ファイル内で、
classes
ディレクトリとキャッシュにどのファイルがどこにあるか。したがって、ロードしようとすると、User
クラスにあるかどうかは関係ありませんclasses/User.php
またはclasses/Models/User.php
またはclasses/Utility/User.php
. 。見つかったらUser.php
どこかのclasses
ディレクトリに含めると、どのファイルを含めるべきかがわかります。User
クラスを自動ロードする必要があります。
@ケビン:
spl_autoload_register は、複数のローダーを定義でき、互いに競合しないため、__autoload よりも優れた代替手段であることを指摘したかっただけです。__autoload 関数を定義するライブラリも含める必要がある場合に便利です。
本気ですか?の ドキュメンテーション 違うことを言います:
コードに既存の __autoload 関数がある場合、この関数を __autoload スタックに明示的に登録する必要があります。これは、spl_autoload_register() が __autoload 関数のエンジン キャッシュを spl_autoload() または spl_autoload_call() のいずれかによって効果的に置き換えるためです。
=> ライブラリを明示的に登録する必要があります __autoload
同じように。しかし、それを除けば、もちろんあなたの言うことは正しいです。この関数の方がより良い代替手段です。
__autoload
動作しますが、PHP 5 でのみ動作します。