web-dev-qa-db-ja.com

効率的なPHP自動読み込みと命名戦略

最近のほとんどのWeb開発者と同様に、私はWebアプリとサイトのための確実なMVCアーキテクチャの利点を十分に楽しんでいます。 MVCをPHPで実行する場合、オートロードは明らかに非常に便利です。

私は _spl_autoload_register_ のファンになっています。これは、単一の__autoload()関数を単に定義するよりも、それぞれが使用する異なる基本モジュールを組み込む場合、明らかにより柔軟であるためです。独自のオートローディング。ただし、私が作成するロード関数については、これまでにあまり気分がよくなりませんでした。ロードする可能性のあるクラスを探すために、多くの文字列チェックとディレクトリスキャンが必要です。

たとえば、_PATH_APP_として定義された基本パスと、modelsviewsおよび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-ing、文字列チェック、ディレクトリスキャンはすべて効率が悪いようです。改善したいと思います。

他の開発者が採用している可能性のあるファイルの命名とオートローディングの戦略について、私は非常に興味があります。私は、オートローディングの代替手段ではなく、効率的なオートローディングに採用できる優れた手法を具体的に探しています。

38
zombat

これは私が私のすべてのプロジェクトで使用しているものです(最後のプロジェクトのソースから直接持ち上げたものです):

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つのクラスを追加するが、現在取り組んでいるプロジェクトの一部ではない。)

28
Mike Boers

私はこの解決策を見つけました:

私は、クラスライブラリフォルダー(個別のモジュール/システムのサブフォルダーを含む)を走査し、ファイルの内容を解析してクラス定義を探す単一のスクリプトを作成しました。 phpファイル(かなり単純な正規表現パターン)でクラス定義が見つかった場合、シンボリックリンクを作成します。

class_name.php -> actual/source/file.php

これにより、クラス名とメインのシンボリックリンクフォルダーへのパスのみを必要とし、パス/文字列操作を行う必要がない単一の単純なオートロード関数を使用できます。

最良の部分は、ソースコードを完全に再配置するか、新しいサブシステムを追加して、リンク生成スクリプトを実行するだけですべてを自動ロードできることです。

13
grossvogel

効率性が必要な場合は、オートロード機能をまったく使用しないでください。オートロード機能は遅延するためのものです。インクルードファイルをインクルードするときは、明示的なパスを提供する必要があります。オートロード機能がこれらのファイルを見つけることができる場合は、それらを明示的に見つけるようにコーディングできます。コードのビュー部分で作業していて、新しいビュークラスをロードしようとしているときに、autoload関数にそれを処理させると、最初にクラスがモデルクラスであると見なされますか?それは非効率的です。代わりに、コードは次のようになります。

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 ....
}

いずれの場合でも、インクルードが発生した時点で、ロードするクラスに関する最も正確な情報が得られます。その情報を使用してクラスを完全にロードすることが、唯一の効率的なクラスロード戦略です。はい、あなたはより多くのクラス変数または(天国は禁じられています)いくつかのグローバル変数で終わるかもしれません。ただし、これは、クラスのファイルシステムの一部を怠惰にスキャンするよりも優れたトレードオフです。

8
jmucchiello