最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

第4章 探究 Composer 加載原理

2023-09-11 21:13 作者:納西妲愛編程  | 我要投稿

4.1 Composer 介紹與應用

1. 什么是 Composer ?

Composer 是一個依賴的包管理工具, 會在每個項目的基礎上進行依賴包的管理,在你項目中的某個目錄 ( 例如 vendor ) 進行安裝。默認情況下它不會全局安裝任何東西。 如果沒有包管理器會怎樣? 當開發(fā)中需要一個組件包時,這時候你就需要下載引入它,如果使用的組件包又需要另外一個包的話,你又不得不繼續(xù)下載包,而且它們之間依賴的環(huán)境條件你還需要自己去進行解決。但如果有了包管理器,開發(fā)需要使用的包或者包與包之間的依賴關系,都完全可以交給包管理器來統(tǒng)一管理之間的依賴關系。

2. 如何使用?

Linux 下載安裝 Composer : $ curl -s https://getcomposer.org/installer | php? $ sudo mv composer.phar /usr/local/bin/composer? Windows 下載安裝 Wondows 平臺上,我們只需要下載 Composer-Setup.exe 后,一步步安裝即可。需要注意的是,你需要開啟 openssl 配置,我們打開 php 目錄下的 php.ini,將

extension=php_openssl.dll

前面的分號去掉就可以了。 安裝好后,在更改鏡像為阿里云 Composer 全量鏡像 composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ 取消配置:composer config -g --unset repos.packagist 4.2 PHP 自動加載原理

1. 為啥需要自動加載

? 例如,現(xiàn)在有 A.php和 B.php,如果現(xiàn)在我A需要用到 B.php 里面的方法,就需要把 B.php 加載到 A.php 里面來,這樣我 A 才能使用到方法。 何來自動加載?就是當一個 PHP 文件實例化一個類文件,但實例化的文件本身不存在,就需要通過自動加載的方式把不存在的文件給引入到當前類庫中,然后才能進行方法調(diào)用的執(zhí)行。相當于有一個備選的方案來幫助引入 PHP 類文件。

2. PHP 要怎么實現(xiàn)自動加載呢?

首先 PHP 在實例化一個不存在的類庫時,就會觸發(fā)到 spl_autoload_register 注冊的閉包方法,在這個函數(shù)的方法體里面進行方法引入。那為啥能準備的引入文件呢 ? 因為當實例化的文件不存在時,這個類的名字會通過參數(shù)傳到閉包函數(shù)中,閉包函數(shù)的方法體在根據(jù)參數(shù)直接來引入相關類,這樣就可以實例化到相關的文件,然后就可以進行正常的操作。 spl_autoload_register(function($className){ ? if (is_file('./lib/' .$className.'.php')) { ?? require './lib/' . $className.'.php'; } }); 其實在 spl_autoload_register 之前,還有一個 __autoload 的魔術方法來進行類庫的自動引入,只不過該方法在一個類文件中只能引用一次,所以當多個文件需要引入時,就不太方便。故次才有了spl_autoload_register 的方法。 4.3 解析 Composer 加載過程 Composer 的加載是通過 PSR 的編碼規(guī)范來進行自動加載的。 PSR-4 規(guī)范了如何指定文件路徑從而自動加載類的定義。 一個完整的類名需具有以下結(jié)構(gòu) : \<命名空間>\<子命名空間>\<類名> 完整的類名

必須

要有一個頂級命名空間,被稱為 "vendor namespace"。

完整的類名

可以

有一個或多個子命名空間。

完整的類名

必須

有一個最終的類名。

完整的類名中

任意一部分

中的下滑線都是沒有特殊含義的。

完整的類名

可以

由任意大小寫字母組成。

所有類名都

必須

是大小寫敏感的。

示例 : PSR-4 風格 類名:Nahida

命名空間前綴:Zend

文件基目錄:/usr/includes/Zend/

文件路徑:/usr/includes/Zend/Nahida.php

類名:Request 命名空間前綴:SymfonyCore

文件基目錄:./vendor/Symfony/Core/

文件路徑:./vendor/Symfony/Core/Request.php

目錄結(jié)構(gòu) :

class的類文件需要存放到 vendor 當中去,不然就會引入失敗。 按照規(guī)范定義了類庫后。Composer 會做如下操作 : 你有一個項目依賴于若干個庫。

其中一些庫依賴于其他庫。

你聲明你所依賴的東西。

Composer 會找出那個版本的包需要安裝,并安裝它們(將它們下載到你的項目中)

比如:項目需要使用 phpunit 組件 { ? "require": { ?? "phpunit/phpunit":"~6.0", ? } } 然后在 composer require 之后我們只要在項目里面直接 use phpunit 的類即可使用。 執(zhí)行 composer require 時發(fā)生了什么 ? Composer 會找到符合 PR4 規(guī)范的第三方庫的源

將其加載到 vendor 目錄下

初始化頂級域名的映射并寫入到指定的文件里

( 如:'PHPUnit\\Framework\\Assert' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert.php') 寫好一個 autoload 函數(shù),并且注冊到 spl_autoload_register( ) 里

4.4 探索 Composer 源碼 很多框架在初始化的時候都會引入 Composer 來協(xié)助自動加載,以 Laravel 為例,它入口文件 index.php 第一句就是利用 Composer 來實現(xiàn)自動加載功能。

1. 啟動框架

ClassLoader.php : composer 加載類:Composer 自動加載功能的核心類。

autoload_static.php : 頂級命名空間初始化類:用于給核心類初始化頂級命名空間。

autoload_classmap.php : 自動加載的最簡單形式:有完整的命名空間和文件目錄的映射。

autoload_files.php : 用于加載全局函數(shù)的文件:存放各個全局函數(shù)所在的文件路徑名。

autoload_namespaces.php : 符合 PSR0 標準的自動加載文件:存放著頂級命名空間與文件的映射。

autoload_psr4.php : 符合 PSR4 標準的自動加載文件:存放著頂級命名空間與文件的映射。

2. autoload_real 引導類

在 vendor 目錄下的 autoload.php 文件中我們可以看出,程序主要調(diào)用了引導類的靜態(tài)方法 getLoader() 來實現(xiàn)類庫的加載和引用。 public static function getLoader() { ??/***************單例模式********************/ ??if (null !== self::$loader) { return self::$loader; ?} ?? ??/*********獲得自動加載核心類對象********************/ ??spl_autoload_register(array('ComposerAutoloaderInit2c981d0f72838b8ba6448f6427ca961d', 'loadClassLoader'), true, true); ?? ??self::$loader = $loader = new \Composer\Autoload\ClassLoader(); ?? ??spl_autoload_unregister(array('ComposerAutoloaderInit2c981d0f72838b8ba6448f6427ca961d', 'loadClassLoader')); ? ??/***********初始化自動加載核心類對象****************/ ??$useStaticLoader = PHP_VERSION_ID >= 50600 && ??!defined('HHVM_VERSION'); ?? ??if ($useStaticLoader) { require_once __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit2c981d0f72838b8ba6448f6427ca961d::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); ? /***********自動加載全局函數(shù)********************/ if ($useStaticLoader) { $includeFiles =?Composer\Autoload\ComposerStaticInit2c981d0f72838b8ba6448f6427ca961d::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } ?? foreach ($includeFiles as $fileIdentifier => $file) { composerRequire2c981d0f72838b8ba6448f6427ca961d($fileIdentifier, $file); } ? ??return $loader; } 4.4.1 單例 單例模式,保證自動加載類只能有一個。 = 50600 && !defined('HHVM_VERSION'); ?if ($useStaticLoader) { ??require_once __DIR__ . '/autoload_static.php'; ? ??call_user_func(?\Composer\Autoload\ComposerStaticInit2c981d0f72838b8ba6448f6427ca961d::getInitializer($loader)); } else { ???//導入composer定義的命名空間規(guī)則 ???$map = require __DIR__ . '/autoload_namespaces.php'; ???foreach ($map as $namespace => $path) { $loader->set($namespace, $path); ??} //引入psr4的加載規(guī)范,并設置到psr4的數(shù)組當中 ???$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); ??} ?} 以上就是對自動加載類的初始化,主要是給自動加載核心類初始化頂級命名空間映射。 初始化的方法有兩種,根據(jù)系統(tǒng)環(huán)境來進行注冊方式的選擇 : ?1. 使用 autoload_static 進行靜態(tài)初始化; ?2. 包含命名空間、psr4、組件類、File等核心類來做到自動加載類庫的引入,然后在來進行初始化 4.4.4 autoload_static 靜態(tài)初始化 ( PHP >= 5.6 ) 靜態(tài)初始化只支持 PHP 5.6 以上版本并且不支持 HHVM 虛擬機。滿足就進行靜態(tài)方式的初始化。autoload_static.php 這個文件會定義了一個用于靜態(tài)初始化的類,名字叫 ComposerStaticInit2c981d0f72838b8ba6448f6427ca961d,為了避免沖突而加了 hash 值。 prefixLengthsPsr4 = ComposerStaticInit7b790917ce8899df9af8ed53631a1c29::$prefixLengthsPsr4; ? $loader->prefixDirsPsr4 = ComposerStaticInit7b790917ce8899df9af8ed53631a1c29::$prefixDirsPsr4; ? $loader->prefixesPsr0?= ComposerStaticInit7b790917ce8899df9af8ed53631a1c29::$prefixesPsr0; ? $loader->classMap = ComposerStaticInit7b790917ce8899df9af8ed53631a1c29::$classMap; ? ??}, null, ClassLoader::class); } 這個靜態(tài)初始化類的核心就是 getInitializer() 函數(shù),它將自己類中的頂級命名空間映射給了 ClassLoader 類。值得注意的是這個函數(shù)返回的是一個匿名函數(shù),這是為什么呢 ? 因為 ClassLoader 類中的 prefixLengthsPsr4 、prefixDirsPsr4 等等變量都是 private 的。利用匿名函數(shù)的綁定功能就可以將這些 private 變量賦給 ClassLoader 類 里的成員變量。 匿名函數(shù)的綁定功能。 4.4.5 classMap(命名空間映射) __DIR__ . '/../..' . '/app/Console/Kernel.php', 'App\\Exceptions\\Handler' => __DIR__ . '/../..' . '/app/Exceptions/Handler.php', 'App\\Http\\Controllers\\Controller' => __DIR__ . '/../..' . '/app/Http/Controllers/Controller.php', ??'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__ . '/../..' . '/app/Http/Controllers/Auth/RegisterController.php', ...) 直接讓命名空間的全名與目錄映射,也會導致這個數(shù)組比較大。 4.4.6 PSR4 標準頂級命名空間映射數(shù)組 array ( 'YuanShen\\Nahida\\' => 15, ???), ???'S' => array ( 'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Component\\Yaml\\' => 23, 'Symfony\\Component\\VarDumper\\' => 28, ... ?), ... ); ? public static $prefixDirsPsr4 = array ( ???'YuanShen\\Nahida\\' => array ( 0 => __DIR__ . '/..' . '/YuanShen/Nahida-common/src', 1 => __DIR__ . '/..' . '/YuanShen/type-resolver/src', 2 => __DIR__ . '/..' . '/YuanShen/Nahida-docblock/src', ??), ???'Symfony\\Polyfill\\Mbstring\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', ??), ???'Symfony\\Component\\Yaml\\' => array ( 0 => __DIR__ . '/..' . '/symfony/yaml', ??), ...) PSR4 標準頂級命名空間映射用了兩個數(shù)組,第一個是用命名空間的第一個字母作為前綴索引,然后是 頂級命名空間,但是最終并不是文件路徑,而是 頂級命名空間的長度。為什么呢 ? 因為 PSR4 標準是用頂級命名空間目錄替換頂級命名空間,所以獲得頂級命名空間的長度很重要。 說明 : 假如我們找 Symfony\Polyfill\Mbstring\example 這個命名空間,通過前綴索引和字符串匹配我們得到了 26, 這條記錄,鍵是頂級命名空間,值是命名空間的長度。拿到頂級命名空間后去 $prefixDirsPsr4 數(shù)組 獲取它的映射目錄數(shù)組 : 注意: 映射目錄可能不止一條 。

array ( ???0 => __DIR__ . '/..' . '/symfony/polyfill-mbstring', ) 然后我們就可以將命名空間 Symfony\\Polyfill\\Mbstring\\example 前 26 個字符替換成目錄 __DIR__ . '/..' . '/symfony/polyfill-mbstring ,就可以得到 __DIR__ . '/..' . '/symfony/polyfill-mbstring/example.php,先驗證磁盤上這個文件是否存在,如果不存在接著遍歷。如果遍歷后沒有找到,則加載失敗。 4.4.7 ClassLoader 接口初始化( PHP < 5.6 ) 如果 PHP 版本低于 5.6 或者使用 HHVM 虛擬機環(huán)境,就要使用核心類的各個方法來進行命令空間、核心類等來進行初始化。不在使用靜態(tài)調(diào)用方式。 $path) { ???$loader->set($namespace, $path); ?} ? ??// PSR4 標準 ??$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); ?} 4.4.8 PSR4 標準的映射 autoload_psr4.php 的頂級命名空間映射。 array($vendorDir . '/dnoegel/php-xdg-base-dir/src'), ? ??'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'), ? ??'TijsVerkoyen\\CssToInlineStyles\\' => array($vendorDir . '/tijsverkoyen/css-to-inline-styles/src'), ? ??'Tests\\' => array($baseDir . '/tests'), ? ??'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), ?... ) PSR4 標準的初始化接口 : fallbackDirsPsr4 = (array) $paths; } else { ? $length = strlen($prefix); ? if ('\\' !== $prefix[$length - 1]) { throw new \InvalidArgumentException( "A non-empty PSR-4 prefix must end with a namespace separator." ); ? } ? $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; ? $this->prefixDirsPsr4[$prefix] = (array) $paths; } ?} 總結(jié)上面頂級命名空間的映射過程 : ( 前綴 -> 頂級命名空間,頂級命名空間 -> 頂級命名空間長度 ) ( 頂級命名空間 -> 目錄 ) 這兩個映射數(shù)組。具體形式也可以查看下面的 autoload_static 的 $prefixLengthsPsr4 、 $prefixDirsPsr4。 4.4.9 命名空間映射 autoload_classmap : __DIR__ . '/../..' . '/app/Console/Kernel.php', ? ??'App\\Exceptions\\Handler' => __DIR__ . '/../..' . '/app/Exceptions/Handler.php', ?... ) addClassMap : classMap) { ? $this->classMap = array_merge($this->classMap, $classMap); } else { ? $this->classMap = $classMap; } ?}

自動加載核心類 ClassLoader 的靜態(tài)初始化到這里就完成了

以上說是 5 部分,真正重要的就兩部分——初始化與注冊。初始化負責頂層命名空間的目錄映射,注冊負責實現(xiàn)頂層以下的命名空間映射規(guī)則。 4.4.10 注冊

上文說到 Composer 自動加載功能的啟動與初始化,經(jīng)過啟動與初始化,自動加載核心類對象已經(jīng)獲得了頂級命名空間與相應目錄的映射,也就是說,如果有命名空間 App\Console\Kernel,我們就可以找到它對應的類文件所在位置。那么,它是什么時候被觸發(fā)去找的呢 ? 這就是 composer 自動加載的核心了,我們先回顧一下自動加載引導類 : public static function getLoader() { ??/***********單例模式********************/ ??if (null !== self::$loader) { return self::$loader; ?} ?? ??/*******獲得自動加載核心類對象********************/ ??spl_autoload_register(array('ComposerAutoloaderInit2c981d0f72838b8ba6448f6427ca961d', 'loadClassLoader'), true, true); ?? ??self::$loader = $loader = new \Composer\Autoload\ClassLoader(); ?? ??spl_autoload_unregister(array('ComposerAutoloaderInit2c981d0f72838b8ba6448f6427ca961d', 'loadClassLoader')); ? ??/***********初始化自動加載核心類對象********************/ ??$useStaticLoader = PHP_VERSION_ID >= 50600 && ??!defined('HHVM_VERSION'); ?? ??if ($useStaticLoader) { require_once __DIR__ . '/autoload_static.php'; call_user_func(\Composer\Autoload\ComposerStaticInit2c981d0f72838b8ba6448f6427ca961d::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); ? ??/************自動加載全局函數(shù)********************/ ??if ($useStaticLoader) { $includeFiles =?Composer\Autoload\ComposerStaticInit2c981d0f72838b8ba6448f6427ca961d::$files; ?} else { $includeFiles = require __DIR__ . '/autoload_files.php'; ?} ?? ??foreach ($includeFiles as $fileIdentifier => $file) { composerRequire2c981d0f72838b8ba6448f6427ca961d($fileIdentifier, $file); ?} ? ??return $loader; } 現(xiàn)在我們開始第四部分:注冊自動加載核心類對象。先來看看核心類的 register() 函數(shù) : public function register($prepend = false) { ??spl_autoload_register(array($this, 'loadClass'), true, $prepend); } 重心都在自動加載核心類 ClassLoader 的 loadClass() 函數(shù)上 : public function loadClass($class) { if ($file = $this->findFile($class)) { ? includeFile($file); ? ? return true; } } 該函數(shù)負責按照 PSR 標準將頂層命名空間以下的內(nèi)容轉(zhuǎn)為對應的目錄,也就是上面所說的將 App\Console\Kernel 中Console\Kernel 這一段轉(zhuǎn)為目錄,至于怎么轉(zhuǎn),會在下面 “運行” 的部分講。核心類 ClassLoader 將 loadClass( ) 函數(shù)注冊到 PHP SPL 中的 spl_autoload_register( ) 里面去。這樣,每當 PHP 遇到一個不認識的命名空間的時候,PHP 會自動調(diào)用注冊到 spl_autoload_register 里面的 loadClass() 函數(shù),然后找到命名空間對應的文件。 4.4.11 全局函數(shù)的自動加載 Composer 不止可以自動加載命名空間,還可以加載全局函數(shù)。怎么實現(xiàn)的呢 ? 把全局函數(shù)寫到特定的文件里面去,在程序運行前挨個 require就行了。這個就是 composer自動加載的第五步,加載全局函數(shù)。 if ($useStaticLoader) { ??$includeFiles = Composer\Autoload\ComposerStaticInit7b790917ce8899df9af8ed53631a1c29::$files; } else { ??$includeFiles = require __DIR__ . '/autoload_files.php'; } ? foreach ($includeFiles as $fileIdentifier => $file) { ?composerRequire2c981d0f72838b8ba6448f6427ca961d($fileIdentifier, $file); } 跟核心類的初始化一樣,全局函數(shù)自動加載也分為兩種:

靜態(tài)初始化和普通初始化

,靜態(tài)加載只支持 PHP5.6 以上并且不支持 HHVM。 4.4.12 靜態(tài)初始化 ComposerStaticInit2c981d0f72838b8ba6448f6427ca961d::$files ? public static $files = array ( '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', ... ); 4.4.13 普通初始化 autoload_files : $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); ?? return array( '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', ?.... ); 其實跟靜態(tài)初始化區(qū)別不大。 4.4.14 加載全局函數(shù) class composerRequire2c981d0f72838b8ba6448f6427ca961d{ ?public static function getLoader(){ ??... ???foreach ($includeFiles as $fileIdentifier => $file) {?? ?????composerRequire7b790917ce8899df9af8ed53631a1c29($fileIdentifier, $file); ??} ??... } } //根據(jù)獲取到全局函數(shù)的數(shù)組來引入相關文件 function composerRequire2c981d0f72838b8ba6448f6427ca961d($fileIdentifier, $file) { ??if (empty(\$GLOBALS['__composer_autoload_files'][\$fileIdentifier])) { require $file; ? $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; ?} } 4.4.15 運行 前面說過,ClassLoader 的 register()函數(shù)將 loadClass()函數(shù)注冊到 PHP 的 SPL 函數(shù)堆棧中,每當 PHP 遇到不認識的命名空間時就會調(diào)用函數(shù)堆棧的每個函數(shù),直到加載命名空間成功。所以 loadClass( ) 函數(shù)就是自動加載的關鍵了。 看下 loadClass( ) 函數(shù) : public function loadClass($class) { ??if ($file = $this->findFile($class)) { includeFile($file); ? return true; ?} } ? public function findFile($class) { ??// work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731 ??if ('\\' == $class[0]) { $class = substr($class, 1); ?} ? ??// class map lookup ??if (isset($this->classMap[$class])) { return $this->classMap[$class]; ?} ??if ($this->classMapAuthoritative) { return false; ?} ? ??$file = $this->findFileWithExtension($class, '.php'); ? ??// Search for Hack files if we are running on HHVM ??if ($file === null && defined('HHVM_VERSION')) { $file = $this->findFileWithExtension($class, '.hh'); ?} ? ??if ($file === null) { // Remember that this class does not exist. return $this->classMap[$class] = false; ?} ? ??return $file; } 在loadClass()中 ,主要調(diào)用findFile()函數(shù)。findFile()在解析命名空間的時候主要分為兩部分 :classMap和findFileWithExtension() 函數(shù)。 classMap 就是檢查命名空間是否在映射數(shù)組中。 findFileWithExtension()函數(shù)包含了 PSR0 和 PSR4 標準的實現(xiàn)。還有個值得注意的是查找路徑成功后includeFile() 仍然是外面的函數(shù),并不是 ClassLoader 的成員函數(shù),原理跟上面一樣,防止有用戶寫 $this 或 self。還有就是如果命名空間是以\開頭的,要去掉\然后再匹配。 看下 findFileWithExtension 函數(shù) : private function findFileWithExtension($class, $ext) { ??// PSR-4 lookup ??$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext; ?? ??$first = $class[0]; ??if (isset($this->prefixLengthsPsr4[$first])) { foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) { ? if (0 === strpos($class, $prefix)) { foreach ($this->prefixDirsPsr4[$prefix] as $dir) { ? if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { 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; ?} } 4.4.16 總結(jié) 原理流程 : 如果我們在代碼中寫下 new YuanShen\Nahida\Element(),PHP 會通過 SPL_autoload_register 調(diào)用 loadClass -> findFile -> findFileWithExtension。步驟如下 : 將 \ 轉(zhuǎn)為文件分隔符 /,加上后綴 php,變成 $logicalPathPsr4, 即 YuanShen//Nahida//Element.php

利用命名空間第一個字母s作為前綴索引搜索 prefixLengthsPsr4 數(shù)組,查到下面這個數(shù)組 :

's' => array ( 'YuanShen\\Nahida\\' => 15, 'YuanShen\\Fake\\' => 13, ) 遍歷這個數(shù)組,得到兩個頂層命名空間 YuanShen\Nahida\ 和 YuanShen\Fake\

在這個數(shù)組中查找 YuanShen\Nahida\Element,找出 YuanShen\Nahida\ 這個頂層命名空間并且長度為 15

在 prefixDirsPsr4 映射數(shù)組中得到 YuanShen\Nahida\ 的目錄映射為 :

'YuanShen\\Nahida\\' => array ( ??0 => __DIR__ . '/..' . '/YuanShen/Nahida-common/src', ??1 => __DIR__ . '/..' . '/YuanShen/type-resolver/src', ??2 => __DIR__ . '/..' . '/YuanShen/Nahida-docblock/src', ), 遍歷這個映射數(shù)組,得到三個目錄映射

查看 “目錄+文件分隔符 //+substr($logicalPathPsr4, $length)” 文件是否存在,存在即返回。這里就是

'__DIR__/../YuanShen/Nahida-common/src + substr(YuanShen/Nahida/Element.php,15)'

如果失敗,則利用 fallbackDirsPsr4 數(shù)組里面的目錄繼續(xù)判斷是否存在文件

如果自己在使用第三方包的過程中,自己定義的根命名空間和第三方包的命名空間一樣,則會以數(shù)組的形式存儲,如 : 'Fairy\\' => array($baseDir . '/app', $vendorDir . '/cshaptx4869/work/src/Fairy'), 但注意不要有文件名沖突,不然只能識別一處,所以最好就是根命名空間不要同名。

第4章 探究 Composer 加載原理的評論 (共 條)

分享到微博請遵守國家法律
阿拉善右旗| 蒙山县| 黄骅市| 措美县| 土默特右旗| 威海市| 扬州市| 西盟| 五台县| 林芝县| 东明县| 甘德县| 苗栗市| 桃园市| 南充市| 桂东县| 济源市| 宜州市| 崇左市| 太湖县| 南投县| 万载县| 新乡市| 江川县| 黄大仙区| 辛集市| 蛟河市| 泰和县| 措勤县| 大宁县| 广汉市| 濉溪县| 睢宁县| 灵川县| 苍梧县| 东城区| 新建县| 黑山县| 巴彦县| 平邑县| 汉阴县|