詳解composer自動加載機制

下面由composer教程欄目給大家由淺入深的介紹composer自動加載機制,希望對需要的朋友有所幫助!

詳解composer自動加載機制

前言

由于對于composer自動加載機制的記憶只剩下了”spl_auto???”和”根據命名空間來推導出文件路徑”這兩個了。。。還是殘缺的。。

本想網上收藏一篇詳解,奈何,沒發現符合我覺得的”由淺入深”文章。
所以有了這篇筆記。

以下知識點即將趕來:
1.了解一下spl_autoload_register
2.composer update發生的故事
3.追蹤一下composer的自動加載

正文

1.了解一下spl_autoload_register

首先查一下php官方手冊:
詳解composer自動加載機制
(偷懶可以只看紅色部分即可)

是不是看著一知半解?
來用白話文來翻譯一下:

我們new一個類的話,必須先require或者include類的文件,如果沒有加載進來則會報錯。這產生一個問題:那這樣的話文件的頭部到處都是requies和include,明顯不符合程序員必須”偷懶”尿性。為了不需要require或者include類文件也能正常的new一個類,出現了自動加載機制。spl_autoload_register這個函數就專門干這個事的。

從截圖得知,此函數有三個參數:

參數 詳解
autoload_function 這里填的是一個***”函數”的名稱***,字符串或者數組,這個函數的功能就是把需要new的文件require或者include盡量,避免new的時候報錯。簡單的說就是要你封裝一個***自動加載文件的函數***
throw 當自動加載的函數無法注冊的時候,是否拋異常
prepend 是否添加函數到函數隊列之首,如果是true則為首,否則尾部

來一波代碼,印象深刻一些:

//文件 testClass.php ,即將new的類 class TestClass{     public function __construct() {         echo '你已經成功new了我了';     } }  //文件autoloadDemo.php文件 spl_autoload_register('autoLoad_function', true, true); function autoLoad_function($class_name){     echo "所有的require或者include文件工作都交給我吧!  ";     $class_filename = "./{$class_name}.php";     echo "我來加載{$class_filename}文件  ";     require_once("./{$class_name}.php"); } $obj_demo = new TestClass();

輸出:

所有的require或者include文件工作都交給我吧! 我來加載testClass.php文件 你已經成功new了我了

明白了這個加載的原理,看下文就順利多了。

2.composer update發生的故事

將自動加載之前,必須要先說一下composer update,這里頭承載了自動加載的前提。

composer項目都包含一個composer.json的配置文件。
詳解composer自動加載機制
這里頭有一個關鍵的字段”autoload”,包含psr-4和files兩個字段。

psr-4:說明是基于psr-4規范的類庫,都支持自動加載,只要在后面的對象中以**“命名空間:路徑”**的方式寫入自己的類庫信息即可。
files:這就就更直接了,寫入路徑就自動加載。

按照以上配置每回composer update之后呢,都會更新一個很重要的文件:./vender/composer/autoload_psr4.php。
詳解composer自動加載機制

這個文件只做了一件事情:把命名空間和文件路徑對應起來,這樣后續自動加載就有映射根據了。

3.追蹤一下composer的自動加載

composer的故事從唯一的一個require說起:

require '../vendor/autoload.php'

這個腳本執行了一個函數:

ComposerAutoloaderInitd9b31141b114fcbee3cf55d0e97b7f87::getLoader()

繼續跟getloader函數做了什么?

public static function getLoader() {    if (null !== self::$loader) {         return self::$loader;     }      spl_autoload_register(array('ComposerAutoloaderInitd9b31141b114fcbee3cf55d0e97b7f87', 'loadClassLoader'), true, true);     self::$loader = $loader = new ComposerAutoloadClassLoader();     spl_autoload_unregister(array('ComposerAutoloaderInitd9b31141b114fcbee3cf55d0e97b7f87', 'loadClassLoader'));      $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());     if ($useStaticLoader) {         require_once __DIR__ . '/autoload_static.php';          call_user_func(ComposerAutoloadComposerStaticInitd9b31141b114fcbee3cf55d0e97b7f87::getInitializer($loader));     } else {         $map = require __DIR__ . '/autoload_namespaces.php';         foreach ($map as $namespace => $path) {             $loader->set($namespace, $path);         }          $map = require __DIR__ . '/autoload_psr4.php';         foreach ($map as $namespace => $path) {             $loader->setPsr4($namespace, $path);         }          $classMap = require __DIR__ . '/autoload_classmap.php';         if ($classMap) {             $loader->addClassMap($classMap);         }     }      $loader->register(true);      if ($useStaticLoader) {         $includeFiles = ComposerAutoloadComposerStaticInitd9b31141b114fcbee3cf55d0e97b7f87::$files;     } else {         $includeFiles = require __DIR__ . '/autoload_files.php';     }     foreach ($includeFiles as $fileIdentifier => $file) {         composerRequired9b31141b114fcbee3cf55d0e97b7f87($fileIdentifier, $file);     }      return $loader; }

這個函數主要做了兩件事情:
1.將各種存有命名空間和文件映射關系的文件autoload_xxx.php加載了進來,并作了一些處理(比如:setPsr4將相關映射加載了進去,這個留意下,下文會有呼應。)。
2.注冊了函數register

繼續跟蹤register做了什么:

public function register($prepend = false) {    spl_autoload_register(array($this, 'loadClass'), true, $prepend); }

原來調用了spl_autoload_register函數,當類沒加載的時候使用loadClass來加載類。(這個前文講的很清楚了,應該很熟了)

繼續跟蹤loadClass實現:

public function loadClass($class) { 	if ($file = $this->findFile($class)) { 		includeFile($file); 		return true; 	} }

大概可以看出,是做了文件的include。
繼續跟蹤下是怎么查找文件的,看findFile函數:

public function findFile($class) {     // class map lookup     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;         }     }      $file = $this->findFileWithExtension($class, '.php');      // Search for Hack files if we are running on HHVM     if (false === $file && defined('HHVM_VERSION')) {         $file = $this->findFileWithExtension($class, '.hh');     }      if (null !== $this->apcuPrefix) {         apcu_add($this->apcuPrefix.$class, $file);     }      if (false === $file) {         // Remember that this class does not exist.         $this->missingClasses[$class] = true;     }      return $file; }

這個函數做了一件事:就是尋找類從上文的autoload_xxx.php初始化的數據中來尋找映射的文件路徑。
其中這個函數findFileWithExtension,適用于尋找psr-4規范的文件的映射信息的。

繼續跟蹤findFileWithExtension:

private function findFileWithExtension($class, $ext) {     // PSR-4 lookup     $logicalPathPsr4 = strtr($class, '', DIRECTORY_SEPARATOR) . $ext;      $first = $class[0];     if (isset($this->prefixLengthsPsr4[$first])) {         $subPath = $class;         while (false !== $lastPos = strrpos($subPath, '')) {             $subPath = substr($subPath, 0, $lastPos);             $search = $subPath.'';             if (isset($this->prefixDirsPsr4[$search])) {                 $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);                 foreach ($this->prefixDirsPsr4[$search] as $dir) {                     if (file_exists($file = $dir . $pathEnd)) {                         return $file;                     }                 }             }         }     }      // PSR-4 fallback dirs     foreach ($this->fallbackDirsPsr4 as $dir) {         if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {             return $file;         }     }      // PSR-0 lookup     if (false !== $pos = strrpos($class, '')) {         // namespaced class name         $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)             . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);     } else {         // PEAR-like class name         $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;     }      if (isset($this->prefixesPsr0[$first])) {         foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {             if (0 === strpos($class, $prefix)) {                 foreach ($dirs as $dir) {                     if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {                         return $file;                     }                 }             }         }     }      // PSR-0 fallback dirs     foreach ($this->fallbackDirsPsr0 as $dir) {         if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {             return $file;         }     }      // PSR-0 include paths.     if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {         return $file;     }      return false; }

這個函數做了件事:將命名空間類這樣的類名,轉換成目錄名/類名.php這樣的路徑,再從前文setPsr4設置的映射信息中尋找映射信息,然后完成返回路徑。

至此composer的自動加載機制結束。

? 版權聲明
THE END
喜歡就支持一下吧
點贊14 分享