前言
平時工作中,一直在使用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文件中),導致剛開始有點懵,由於自己平時這塊使用的比較少,所以需要再這塊進行加強,平時看了一些事件機制實現,大部分都是觀察者模式進行綁定和通知的。