ThinkPHP5.1ソースコードの浅い分析(二)自動ロードメカニズム


ライフサイクル第2編に引き続き、ハトの文章を勝手に書かないで安心してください.
第1編では、エントランススクリプトについてもお話ししましたが、自動ロード機能も登録されています
  • この文書のデフォルトでは、自動ロードとネーミングスペースの基礎があります.この記事を参照してくださいphpクラスの自動ロードとネーミングスペース
  • オートローディングメカニズム
    phpの自動ロードは Loaderクラスで実現され,このクラスはbase.phpに導入された.
    //base .php
    //   Loader 
    require __DIR__ . '/library/think/Loader.php';
    
    //       
    Loader::register();
    

    私たちのプログラムはここでLoaderの静的メソッドを実行し、同時にこれもすべてのクラスregister()です.私たちはLoader.phpに入り、上の実行順序でそのコアが何であるかを見てみましょう.
    register()メソッド実行プロセス
    登録システムの自動ロード
    このメソッドは行数が長すぎます.少しずつ分析します.
    //         
            spl_autoload_register($autoload ?: 'think\\Loader::autoload', true, true);

    これは私たちの自動ロード関数を登録して、$autoloadこの変数は伝達のパラメータで、あなたが自分で自分のロードクラスを実現することができることを考慮して、便利に開拓するために、TPはあなたに自分のクラスのロード方法を実現させることができます.
    この関数を知らない人は、文章の一番上の接続を見てください.詳しく説明してあります.
    Composerオートローディングサポート
    $rootPath = self::getRootPath();
            self::$composerPath = $rootPath . 'vendor' . DIRECTORY_SEPARATOR . 'composer' . DIRECTORY_SEPARATOR;
    
            // Composer      
            if (is_dir(self::$composerPath)) {
                if (is_file(self::$composerPath . 'autoload_static.php')) {
                    require self::$composerPath . 'autoload_static.php';
                    //           
                    $declaredClass = get_declared_classes();
                    $composerClass = array_pop($declaredClass);
    
                    foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) {
                        if (property_exists($composerClass, $attr)) {
                            self::${$attr} = $composerClass::${$attr};
                        }
                    }
                } else {
                    self::registerComposerLoader(self::$composerPath);
                }
            }

    composer拡張をサポートするため、自動登録時にcomposerもついでに登録して、拡張の呼び出しを便利にしました.
    autoload_static.phpの変数をメモリにロードするには、autoload_のための難題があります.static.phpファイルのクラス名は常に変化しており、固定されたクラス名は得られません.(私のシステムのクラス名がComposerStaticInit5109814b18095308ffe89ba7a1be18dfのように)require self::$composerPath . 'autoload_static.php';の属性をプログラムにロードするために、ここでは形式を変えました.
    まず、プログラムにロードされたすべてのクラス名を取得し、最後にロードされたクラス名(配列の最後)を取得します.
    $declaredClass = get_declared_classes(); 
    $composerClass = array_pop($declaredClass);

    クラス名を取得し、property_exists($composerClass, $attr)を呼び出してクラスに指定された属性があるかどうかを確認します.
    質問:composer_staticのパラメータは何を表しますか?
     foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr)      ('fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files')      ?

    ClassMap(ネーミングスペースマッピング)
      public static $classMap = array (
    
          'App\\Http\\Controllers\\Auth\\ForgotPasswordController'
                  => __DIR__ . '/../..' . '/app/Http/Controllers/Auth/ForgotPasswordController.php',
    
          'App\\Http\\Controllers\\Auth\\LoginController'
                  => __DIR__ . '/../..' . '/app/Http/Controllers/Auth/LoginController.php',
    
          'App\\Http\\Controllers\\Auth\\RegisterController'
                  => __DIR__ . '/../..' ,
                  ……
    )

    直接ネーミング空間のフルネームとディレクトリのマッピングは,単純で乱暴であり,この配列もかなり大きい.
    PSR 4標準トップクラスのネーミングスペースマッピング配列:
      public static $prefixLengthsPsr4 = array(
          'p' => array (
            'phpDocumentor\\Reflection\\' => 25,
        ),
          'S' => array (
            'Symfony\\Polyfill\\Mbstring\\' => 26,
            'Symfony\\Component\\Yaml\\' => 23,
            'Symfony\\Component\\VarDumper\\' => 28,
            ...
        ),
      ...);
    
      public static $prefixDirsPsr4 = array (
          'phpDocumentor\\Reflection\\' => array (
            0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',
            1 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',
            2 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src',
        ),
           'Symfony\\Polyfill\\Mbstring\\' => array (
            0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
        ),
          'Symfony\\Component\\Yaml\\' => array (
            0 => __DIR__ . '/..' . '/symfony/yaml',
        ),
      ...)

    PSR 4標準の最上位ネーミングスペースマッピングには2つの配列が使用され、1つ目はネーミングスペースの最初のアルファベットを接頭辞インデックスとして使用し、次に最上位ネーミングスペースとして使用されますが、最終的にはファイルパスではなく、最上位ネーミングスペースの長さです.どうしてですか.
    PSR 4規格は、トップネームスペースディレクトリをトップネームスペースディレクトリに置き換えるため、トップネームスペースの長さを得ることが重要である.
    これらの配列の役割を具体的に説明します.Symfony\Polyfill\Mbstring\exampleというネーミングスペースを探すと、接頭辞インデックスと文字列マッチングで得られます.
    'Symfony\\Polyfill\\Mbstring\\' => 26,

    このレコードは、キーが最上位のネーミングスペースであり、値がネーミングスペースの長さです.上位ネーミングスペースを取得した後、$prefixDirsPsr4配列に行ってマッピングディレクトリ配列を取得します.(マッピングディレクトリは1つではない可能性があります)
     array (
                  0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring',
              )

    次に、ネーミングスペースSymfony\\Polyfill\\Mbstring\\exampleの最初の26文字をディレクトリ__DIR__ . '/..' . '/symfony/polyfill-mbstringに置き換えることができ、ディスク上のこのファイルが存在しないかどうかを確認してから、__DIR__ . '/..' . '/symfony/polyfill-mbstring/example.phpを得ることができます.ループ後に見つからない場合は、ロードに失敗します.
    注:実は1つのwebフレームワークとして、composerの中のものは、ThinkPHPが関心を持つべきではありませんが、TP 5自身のオリジナルのフレームワークパッケージのデザインがcomposerを完全に含んでいないため、登録が自動的にロードされるときにその属性値を持って自分で使用します(自分の理解に限られ、あなたの観点と異なる場合は議論を歓迎します)
    ネームスペース定義の登録
    //         
            self::addNamespace([
                'think'  => __DIR__,
                'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits',
            ]);
    
            //         
            if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
                self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
            }
    
            //     extend  
            self::addAutoLoadDir($rootPath . 'extend');

    この後のコードは大きく異なり、必要なクラスをPsr4 という静的変数にマッピングしています.ネーミングスペースを使用して呼び出すのに便利です.
    //         
            if (is_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php')) {
                self::addClassMap(__include_file($rootPath . 'runtime' . DIRECTORY_SEPARATOR . 'classmap.php'));
            }
    

    TP 5コードでphp think optimize:autoloadを実行するとruntimeでclassmapが生成する.phpファイル、ファイル形式
    return [
        'app\\index\\controller\\Index' => 'D:/app/tp5/application/' . 'index/controller/Index.php',
        'think\\App' => 'D:/app/tp5/thinkphp/library/' . '/think/App.php',
        'think\\Build' => 'D:/app/tp5/thinkphp/library/' . '/think/Build.php',
        'think\\Cache' => 'D:/app/tp5/thinkphp/library/' . '/think/Cache.php',
        'think\\Collection' => 'D:/app/tp5/thinkphp/library/' . '/think/Collection.php',
        ...
        ]

    クラスライブラリマッピングファイルが生成され、runtimeディレクトリの下にclassmap.phpファイルが生成され、生成されたクラスライブラリマッピングファイルはシステムディレクトリとアプリケーションディレクトリのクラスライブラリをスキャンします.その後出会ったらそのまま持ってきて、システムの自動ロードの性能を高めます.register()関数ここでは大体解析が終わります.ここでは登録自動ロードについて説明します.
    自動ロードの使用registerにおいて,関数式Loader::autoload()を自動的にロードする方法を定義した.私たちのbase.phpでは、自動ロードメカニズムをロードすると、異常処理がロードされます.
    //   Loader 
    require __DIR__ . '/library/think/Loader.php';
    
    //       
    Loader::register();
    
    //            
    Error::register();

    この状態ではErrorは存在せず,我々の自動ロードメソッドに入って再試行する.
    //      
    public static function autoload($class)
        {
            if (isset(self::$classAlias[$class])) {
                return class_alias(self::$classAlias[$class], $class);
            }
    
            if ($file = self::findFile($class)) {
    
                // Win         
                if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
                    return false;
                }
    
                __include_file($file);
                return true;
            }
        }

    断片を切り取って少しずつ分析します.
    if (isset(self::$classAlias[$class])) {
                return class_alias(self::$classAlias[$class], $class);
            }

    このセクションでは、クラスに別名を設定するかどうかを判断しますが、明らかに設定されていません.
    if ($file = self::findFile($class)) {
    
                // Win         
                if (strpos(PHP_OS, 'WIN') !== false && pathinfo($file, PATHINFO_FILENAME) != pathinfo(realpath($file), PATHINFO_FILENAME)) {
                    return false;
                }
    
                __include_file($file);
                return true;
            }
    findFile($class) classMapをruntimeフォルダの下にキャッシュした場合、彼は直接戻ります.(これはclassMapをキャッシュするとパフォーマンスが向上する理由です)、キャッシュがなければマッピング関係を格納していた静的配列prefixDirsPsr4prefixLengthsPsr4を組み合わせてファイルのディレクトリを探すと、速度が比較的遅くなります.見つからなければ空に戻り、 spl_autoload_registerはクラスが見つからないと判断し、エラーを投げ出す.
    見つかったらlinuxとwindowのパス名の違いを解消します.(linuxは大文字と小文字を厳密に区別しますが、winは厳密に区別しません)
    ここでは主にwindow環境でパス名の大文字と小文字が区別されないことを心配しているので、linuxのディレクトリルールに基づいてファイルパスを書き直しました.
    後でディレクトリファイルを追加します