composer--------psr4加載流程解析


前言

平時工作中,一直在使用composer解決一些包依賴管理,自動加載等業務場景,但是一直沒有好好看過vendor/composer目錄下面的文件,今天看了下源碼,也算清楚了內部的文件執行流程。

主要文件:

    vendor/autoload.php    入口文件

    vendor/composer/autoload_real.php    真正加載文件

   vendor/composer/ClassLoader.php       內部加載器文件

    vendor/composer/autoload_static.php   當php版本大於等於5.6時,內部加載器會用此文件里面的配置映射信息填充相關數組

   vendor/composer/autoload_classmap.php   當php版本小於5.6時,內部加載器會用此文件里面的配置映射信息填充加載器的classMap數組

   vendor/composer/autoload_files.php   當php版本小於5.6時,真正加載文件會在內部直接require進autoload_files文件里面的所有文件

   vendor/composer/autoload_psr4.php   當php版本小於5.6時,包含符合psr4標准的所有    命名空間:對應查找路徑 的映射信息

    

psr4標准自動加載案例分析:

1、入口文件,我使用了Think\wahaha\Chu這個測試類

<?php
require './composer/vendor/autoload.php';
use Think\wahaha\Chu;

echo Chu::getName();

2、composer.json中的psr4配置信息

"autoload": {
   "psr-4": {                    
       "Think\\": "think/"
   }
}

3、目錄結構

think

    wahaha

        Chu.php    

vendor

        autoload.php

        composer

            ClassLoader.php

            .......

4、自動加載類使用時,內部文件執行過程分析

  • 入口文件加載autoload.php,而autoload.php該文件引入autoload_real.php,執行了autoload_real.php文件中類的getLoader()靜態方法
    private static $loader;

    //引入類加載器文
    public static function loadClassLoader($class)
    {
        if ('Composer\Autoload\ClassLoader' === $class) {
            require __DIR__ . '/ClassLoader.php';
        }
    }

    public static function getLoader()
    {
        //判斷類加載器實例是否存在
        if (null !== self::$loader) {
            return self::$loader;
        }
        
        //注冊類本身的loadClassLoader方法為自動加載方法
        spl_autoload_register(array('ComposerAutoloaderInitf4cf8dfb98c23a8977f6da4c2c099d38', 'loadClassLoader'), true, true);
        //實例化類加載器
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInitf4cf8dfb98c23a8977f6da4c2c099d38', 'loadClassLoader'));
        //判斷運行環境信息
        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
        
        if ($useStaticLoader) {//php大於5.6時
            /*
            加載classmap,files,psr4相關映射配置文件
            類加載器相關數組配置信息初始化
            prefixLengthsPsr4、classMa、prefixDirsPsr4
            */
            require_once __DIR__ . '/autoload_static.php';
//調用返回的回調函數對象,其中回調函數中進行類加載器實例的一些相關數組配置信息初始化
call_user_func(\Composer\Autoload\ComposerStaticInitf4cf8dfb98c23a8977f6da4c2c099d38::getInitializer($loader)); } else {//php小於5.6時 //加載命名空間與查找路徑映射配置信息文件,並將類加載器相關數組配置初始化 $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } //加載psr4標准的命名空間與查找路徑映射配置信息文件,並將類加載器相關數組配置初始化 $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } //加載ClassMap映射配置信息文件,並將類加載器相關數組配置初始化 $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } } //注冊類加載器實例的loadClass方法為自動加載方法,實現類的自動加載 $loader->register(true); //獲取files映射信息 if ($useStaticLoader) { $includeFiles = Composer\Autoload\ComposerStaticInitf4cf8dfb98c23a8977f6da4c2c099d38::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } //加載files數組中的相關文件 foreach ($includeFiles as $fileIdentifier => $file) { composerRequiref4cf8dfb98c23a8977f6da4c2c099d38($fileIdentifier, $file); } return $loader; }
  • 看看類加載器ClassLoader.php文件中的loadclass方法里做了什么

    

public function loadClass($class)
{
        //查找類名對應的實際文件地址,並引入該文件
        if ($file = $this->findFile($class)) {
            includeFile($file);

            return true;
        }
}

public function findFile($class)
{
    // 查找類是否存在classMap數組中,如果在就返回類的文件路徑
        if (isset($this->classMap[$class])) {
            return $this->classMap[$class];
        }
        if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
            return false;
        }
        if (null !== $this->apcuPrefix) {
            $file = apcu_fetch($this->apcuPrefix.$class, $hit);
            if ($hit) {
                return $file;
            }
        }
        
        //psr4標准查找
        $file = $this->findFileWithExtension($class, '.php');
       ..............
}

private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;//將類名拼接上文件擴展名  Think\wahaha\Chu.php   

        $first = $class[0];//獲取類名的首字母   T
        if (isset($this->prefixLengthsPsr4[$first])) {//通過首字符查找對應的項    例如:[T=>['Think\\'=>6]],詳情可查看autoload_static.php文件
            $subPath = $class;
            while (false !== $lastPos = strrpos($subPath, '\\')) {//將類名拆分,例如  Think\wahaha\Chu =>    Think\wahaha、Think
                $subPath = substr($subPath, 0, $lastPos);
                $search = $subPath.'\\';                    //    Think\    Think\wahaha\
                if (isset($this->prefixDirsPsr4[$search])) { //通過search項查找對應的目錄數組 例如: ['Think\\'=>[ 0 => __DIR__ . '/../..' . '/think']],,詳情可查看autoload_static.php文件
                    foreach ($this->prefixDirsPsr4[$search] as $dir) {//循環遍歷目錄數組
                        $length = $this->prefixLengthsPsr4[$first][$search];//獲取命名空間字符長度 例如 Think\  長度為6
/*
拼接路徑:
__DIR__ . '/../..' . '/think' / wahaha\Chu.php
*/
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } } } } .......................... }

 

 結語:

    至此,psr4分析差不多就完了,更多細節可以自己參考里面的代碼,源碼中使用了匿名函數類(autoload_static.php文件中),導致剛開始有點懵,由於自己平時這塊使用的比較少,所以需要再這塊進行加強,平時看了一些事件機制實現,大部分都是觀察者模式進行綁定和通知的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM