3

使用する必要があるすべてのクラスにスクリプトからアクセスできるようにするために、PHP スクリプトに非常に多くのファイルを「含める」必要があるという問題を処理するためのベスト プラクティスは何だろうと考えています。

現在、直接アクセスするクラスを含めるためにinclude_onceを使用しています。それらのそれぞれはinclude_once、アクセスするクラスになります。

この関数の使用を検討しました__autoloadが、クラス ファイルをディレクトリ ツリーに編成する予定がある場合、hat はうまく機能しないようです。これを行うと、探しているクラスが見つかるまで、ディレクトリ ツリーをたどることになるようです。 また、これが異なる名前空間で同じ名前のクラスにどのように影響するかはわかりません。

これを処理する簡単な方法はありますか?

または、PHP は、多くの異なるディレクトリにある可能性のある個別のファイルにすべての異なるオブジェクトが配置されている「エンタープライズ」タイプのアプリケーションには適していません。

4

7 に答える 7

6

私のアプリケーションでは、通常、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 関数から autobuilder を実行することもできますが、プログラムが無限ループに陥る可能性があります。

シリアライズされた配列は非常に高速です。

@JasonMichael: PHP 4 は死んでいます。それを乗り越えてください。

于 2008-08-23T09:37:32.237 に答える
2

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;
}
于 2008-08-23T01:33:01.760 に答える
1

また、物理ディレクトリにマップされる構造化された命名規則を使用して、クラス ファイルの場所をプログラムで決定することもできます。これが Zend がZend Frameworkで行う方法です。したがって、呼び出すZend_Loader::loadClass("Zend_Db_Table");と、クラス名がアンダースコアで分割されてディレクトリの配列に分解され、Zend_Loader クラスが必要なファイルをロードします。

すべての Zend モジュールと同様に、ローダーだけを独自のクラスで使用できると思いますが、Zend の MVC を使用するサイトの一部としてのみ使用しました。

しかし、動的なクラス ローディングを使用する場合、負荷がかかった状態でのパフォーマンスについて懸念がありました。たとえば、Zend_Loader とクラス ファイルのハード ローディングを比較したこのブログ記事を参照してください。

PHP のインクルード パスを検索しなければならないというパフォーマンスの低下だけでなく、オペコードのキャッシュも無効になります。その投稿へのコメントから:

ANY ダイナミック クラス ローダーを使用する場合、APC はそれらのファイルを完全にキャッシュできません。ファイルをハードロードすることにより、APC はファイルを完全にキャッシュできます。

于 2008-08-23T10:58:06.257 に答える
0

これまでの提案の中で、私は Kevin のものに部分的に賛成ですが、絶対的なものである必要はありません。__autoload で使用するオプションがいくつかあります。

  1. すべてのクラス ファイルを 1 つのディレクトリに配置します。クラスにちなんでファイルに名前を付けます。つまり、classes/User.phpまたはclasses/User.class.php.
  2. モデルを 1 つのディレクトリに配置し、コントローラを別のディレクトリに配置するという Kevin のアイデア。すべてのクラスが MVC フレームワークにうまく収まる場合はうまく機能しますが、場合によっては面倒です。
  3. クラス名にディレクトリを含めます。たとえば、Model_User というクラスは、実際には に配置されclasses/Model/User.phpます。__autoload 関数は、ファイルを見つけるためにアンダースコアをディレクトリ区切り記号に変換することを知っています。
  4. ディレクトリ構造全体を一度解析するだけです。__autoload 関数で、またはそれが定義されている同じ PHP ファイルでさえ、classesディレクトリの内容をループして、どのファイルがどこにあるかをキャッシュします。したがって、クラスをロードしようとしても、またはまたはUserにあるかどうかは関係ありません。ディレクトリ内のどこかを見つけると、クラスを自動ロードする必要があるときにどのファイルを含めるかがわかります。classes/User.phpclasses/Models/User.phpclasses/Utility/User.phpUser.phpclassesUser
于 2008-08-23T02:34:35.497 に答える
0

__autoloadディレクトリツリー内のどこにあるかを関数に伝える、クラスの一貫した命名規則がある場合はうまく機能します。MVC は、クラスをモデル、ビュー、およびコントローラーに簡単に分割できるため、この種の作業に特に適しています。

または、名前の連想配列をクラスのファイルの場所に保持し、__autoloadこの配列にクエリを実行させます。

于 2008-08-23T00:46:44.273 に答える
0

__autoload動作しますが、PHP 5 でのみです。

于 2008-08-23T00:48:23.460 に答える
0

@ケビン:

複数のローダーを定義でき、互いに競合しないため、 spl_autoload_register は __autoload のより良い代替手段であることを指摘しようとしていました。__autoload 関数も定義するライブラリを含める必要がある場合に便利です。

本気ですか?ドキュメントには別の言い方があります:

コードに既存の __autoload 関数がある場合、この関数を明示的に __autoload スタックに登録する必要があります。これは、spl_autoload_register() が spl_autoload() または spl_autoload_call() によって __autoload 関数のエンジン キャッシュを効果的に置き換えるためです。

=>ライブラリも明示的に登録する必要があり__autoloadます。しかし、もちろんあなたの言うことは別として、この関数はより良い代替手段です。

于 2008-08-23T08:14:10.610 に答える