摘要:任務(wù)是加載類(lèi)的初始化頂級(jí)命名空間與文件路徑映射初始化和注冊(cè)。在實(shí)際情況下可能會(huì)出現(xiàn)這樣的情況。值得注意的是這個(gè)函數(shù)返回的是一個(gè)匿名函數(shù),為什么呢原因就是類(lèi)中的等等都是的。。。關(guān)于匿名函數(shù)的綁定功能。
前言
在開(kāi)始之前,歡迎關(guān)注我自己的博客:www.leoyang90.cn
上一篇文章,我們討論了 PHP 的自動(dòng)加載原理、PHP 的命名空間、PHP 的 PSR0 與 PSR4 標(biāo)準(zhǔn),有了這些知識(shí),其實(shí)我們就可以按照 PSR4 標(biāo)準(zhǔn)寫(xiě)出可以自動(dòng)加載的程序了。然而我們?yōu)槭裁匆约簩?xiě)呢?尤其是有 Composer 這神一樣的包管理器的情況下?
Composer 自動(dòng)加載概論Composer 是 PHP 的一個(gè)依賴(lài)管理工具。它允許你申明項(xiàng)目所依賴(lài)的代碼庫(kù),它會(huì)在你的項(xiàng)目中為你安裝他們。詳細(xì)內(nèi)容可以查看 Composer 中文網(wǎng)。
Composer Composer 將這樣為你解決問(wèn)題:
你有一個(gè)項(xiàng)目依賴(lài)于若干個(gè)庫(kù)。
其中一些庫(kù)依賴(lài)于其他庫(kù)。
你聲明你所依賴(lài)的東西。
Composer 會(huì)找出哪個(gè)版本的包需要安裝,并安裝它們(將它們下載到你的項(xiàng)目中)。
例如,你正在創(chuàng)建一個(gè)項(xiàng)目,你需要一個(gè)庫(kù)來(lái)做日志記錄。你決定使用 monolog。為了將它添加到你的項(xiàng)目中,你所需要做的就是創(chuàng)建一個(gè) composer.json 文件,其中描述了項(xiàng)目的依賴(lài)關(guān)系。
{ "require": { "monolog/monolog": "1.2.*" } }
然后我們只要在項(xiàng)目里面直接use MonologLogger即可,神奇吧!
簡(jiǎn)單的說(shuō),Composer 幫助我們下載好了符合 PSR0 或 PSR4 標(biāo)準(zhǔn)的第三方庫(kù),并把文件放在相應(yīng)位置;幫我們寫(xiě)了 _autoload() 函數(shù),注冊(cè)到了 spl_register() 函數(shù),當(dāng)我們想用第三方庫(kù)的時(shí)候直接使用命名空間即可。
??
那么當(dāng)我們想要寫(xiě)自己的命名空間的時(shí)候,該怎么辦呢?很簡(jiǎn)單,我們只要按照 PSR4 標(biāo)準(zhǔn)命名我們的命名空間,放置我們的文件,然后在 composer 里面寫(xiě)好頂級(jí)域名與具體目錄的映射,就可以享用 composer 的便利了。
當(dāng)然如果有一個(gè)非常棒的框架,我們會(huì)驚喜地發(fā)現(xiàn),在 composer 里面寫(xiě)頂級(jí)域名映射這事我們也不用做了,框架已經(jīng)幫我們寫(xiě)好了頂級(jí)域名映射了,我們只需要在框架里面新建文件,在新建的文件中寫(xiě)好命名空間,就可以在任何地方 use 我們的命名空間了。
下面我們就以 laravel 框架為例,講一講 composer 是如何實(shí)現(xiàn) PSR0 和 PSR4 標(biāo)準(zhǔn)的自動(dòng)加載功能。
首先,我們先大致了解一下 Composer 自動(dòng)加載所用到的源文件。
啟動(dòng)autoload_real.php:自動(dòng)加載功能的引導(dǎo)類(lèi)。任務(wù)是 composer 加載類(lèi)的初始化(頂級(jí)命名空間與文件路徑映射初始化)和注冊(cè)( spl_autoload_register() )。
ClassLoader.php:composer 加載類(lèi)。composer 自動(dòng)加載功能的核心類(lèi)。
autoload_static.php:頂級(jí)命名空間初始化類(lèi),用于給核心類(lèi)初始化頂級(jí)命名空間。
autoload_classmap.php:自動(dòng)加載的最簡(jiǎn)單形式,有完整的命名空間和文件目錄的映射;
autoload_files.php:用于加載全局函數(shù)的文件,存放各個(gè)全局函數(shù)所在的文件路徑名;
autoload_namespaces.php:符合 PSR0 標(biāo)準(zhǔn)的自動(dòng)加載文件,存放著頂級(jí)命名空間與文件的映射;
autoload_psr4.php:符合 PSR4 標(biāo)準(zhǔn)的自動(dòng)加載文件,存放著頂級(jí)命名空間與文件的映射;
laravel 框架的初始化是需要 composer 自動(dòng)加載協(xié)助的,所以 laravel 的入口文件 index.php 第一句就是利用 composer 來(lái)實(shí)現(xiàn)自動(dòng)加載功能。
require __DIR__."/../bootstrap/autoload.php";
咱們接著去看 bootstrap 目錄下的 autoload.php:
define("LARAVEL_START", microtime(true)); require __DIR__."/../vendor/autoload.php";
再去vendor目錄下的autoload.php:
require_once __DIR__ . "/composer" . "/autoload_real.php"; return ComposerAutoloaderInit 832ea71bfb9a4128da8660baedaac82e::getLoader();
為什么框架要在 bootstrap/autoload.php 轉(zhuǎn)一下?個(gè)人理解,laravel 這樣設(shè)計(jì)有利于支持或擴(kuò)展任意有自動(dòng)加載的第三方庫(kù)。
好了,我們終于要看到了 Composer 真正要顯威的地方了。autoload_real 里面就是一個(gè)自動(dòng)加載功能的引導(dǎo)類(lèi),這個(gè)類(lèi)不負(fù)責(zé)具體功能邏輯,只做了兩件事:初始化自動(dòng)加載類(lèi)、注冊(cè)自動(dòng)加載類(lèi)。
到 autoload_real 這個(gè)文件里面去看,發(fā)現(xiàn)這個(gè)引導(dǎo)類(lèi)的名字叫 ComposerAutoloaderInit832ea71bfb9a4128da8660baedaac82e,為什么要叫這么古怪的名字呢?因?yàn)檫@是防止用戶(hù)自定義類(lèi)名跟這個(gè)類(lèi)重復(fù)沖突了,所以在類(lèi)名上加了一個(gè) hash 值。
其實(shí)還有一個(gè)原因,那就是composer運(yùn)行加載多個(gè)ComposerAutoloaderInit類(lèi)。在實(shí)際情況下可能會(huì)出現(xiàn)這樣的情況:vendor/modelA/vendor/composer。也就是說(shuō)第三方庫(kù)中也存在著一個(gè)composer,他有著自己所依賴(lài)的各種庫(kù),也是通過(guò)composer來(lái)加載。這樣的話就會(huì)有兩個(gè)ComposerAutoloaderInit類(lèi),那么就會(huì)觸發(fā)redeclare的錯(cuò)誤。給ComposerAutoloaderInit加上一個(gè)hash,那么就可以實(shí)現(xiàn)多個(gè)class loader 的加載。
在 vendor 目錄下的 autoload.php 文件中我們可以看出,程序主要調(diào)用了引導(dǎo)類(lèi)的靜態(tài)方法 getLoader(),我們接著看看這個(gè)函數(shù)。
public static function getLoader() { /***************************經(jīng)典單例模式********************/ if (null !== self::$loader) { return self::$loader; } /***********************獲得自動(dòng)加載核心類(lèi)對(duì)象********************/ spl_autoload_register(array("ComposerAutoloaderInit 832ea71bfb9a4128da8660baedaac82e", "loadClassLoader"), true, true); self::$loader = $loader = new ComposerAutoloadClassLoader(); spl_autoload_unregister(array("ComposerAutoloaderInit 832ea71bfb9a4128da8660baedaac82e", "loadClassLoader")); /***********************初始化自動(dòng)加載核心類(lèi)對(duì)象********************/ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined("HHVM_VERSION"); if ($useStaticLoader) { require_once __DIR__ . "/autoload_static.php"; call_user_func(ComposerAutoloadComposerStaticInit 832ea71bfb9a4128da8660baedaac82e::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); } } /***********************注冊(cè)自動(dòng)加載核心類(lèi)對(duì)象********************/ $loader->register(true); /***********************自動(dòng)加載全局函數(shù)********************/ if ($useStaticLoader) { $includeFiles = ComposerAutoloadComposerStaticInit 832ea71bfb9a4128da8660baedaac82e::$files; } else { $includeFiles = require __DIR__ . "/autoload_files.php"; } foreach ($includeFiles as $fileIdentifier => $file) { composerRequire 832ea71bfb9a4128da8660baedaac82e($fileIdentifier, $file); } return $loader; }
從上面可以看出,我把自動(dòng)加載引導(dǎo)類(lèi)分為5個(gè)部分。
第一部分——單例第一部分很簡(jiǎn)單,就是個(gè)最經(jīng)典的單例模式,自動(dòng)加載類(lèi)只能有一個(gè),多次加載影響效率,可能會(huì)引起重復(fù)require同一個(gè)文件。
if (null !== self::$loader) { return self::$loader; }第二部分——構(gòu)造 ClassLoader 核心類(lèi)
第二部分 new 一個(gè)自動(dòng)加載的核心類(lèi)對(duì)象。
/***********************獲得自動(dòng)加載核心類(lèi)對(duì)象********************/ spl_autoload_register(array("ComposerAutoloaderInit 832ea71bfb9a4128da8660baedaac82e", "loadClassLoader"), true, true); self::$loader = $loader = new ComposerAutoloadClassLoader(); spl_autoload_unregister(array("ComposerAutoloaderInit 832ea71bfb9a4128da8660baedaac82e", "loadClassLoader"));
loadClassLoader() 函數(shù):
public static function loadClassLoader($class) { if ("ComposerAutoloadClassLoader" === $class) { require __DIR__ . "/ClassLoader.php"; } }
從程序里面我們可以看出,composer 先向 PHP 自動(dòng)加載機(jī)制注冊(cè)了一個(gè)函數(shù),這個(gè)函數(shù) require 了 ClassLoader 文件。成功 new 出該文件中核心類(lèi) ClassLoader() 后,又銷(xiāo)毀了該函數(shù)。
為什么不直接 require,而要這么麻煩?原因和ComposerAutoloaderInit加上hash一樣,如果直接require,那么會(huì)造成ClassLoader類(lèi)的重復(fù)定義。所以有人建議這樣:
if (!class_exists("ComposerAutoloadClassLoader", false)) { require __DIR__ . "/ClassLoader.php"; } static::$loader = $loader = new ComposerAutoloadClassLoader();
其實(shí)這樣可以更加直觀。但是class_exists有個(gè)缺點(diǎn),那就是opcache緩存有個(gè)bug,class_exists即使為真,程序仍然會(huì)進(jìn)入if條件進(jìn)行require,這樣仍然造成了重復(fù)定義的問(wèn)題。
那為什么不跟引導(dǎo)類(lèi)一樣用個(gè) hash 呢?這樣就可以多次定義這個(gè)ClassLoader類(lèi)了。原因就是這個(gè)類(lèi)是可以復(fù)用的,框架允許用戶(hù)使用這個(gè)類(lèi),如果用hash用戶(hù)就完全沒(méi)辦法用ClassLoader了。
所以最終的解決方案就是利用spl_autoload_register來(lái)加載,這樣只要ClassLoader只要被聲明過(guò),spl_autoload_register就不會(huì)調(diào)用,也就不會(huì)require。
可見(jiàn)這簡(jiǎn)單的幾行代碼其實(shí)內(nèi)幕很深的。詳細(xì)可見(jiàn)
github 的相關(guān) issue:Unable to run tests with phpunit and composer installed globally #1248
github 相關(guān)解決方案 PR : Allow loading of multiple composer autoloaders concurrently, fixes #1248 #1313
/***********************初始化自動(dòng)加載核心類(lèi)對(duì)象********************/ $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined("HHVM_VERSION"); if ($useStaticLoader) { require_once __DIR__ . "/autoload_static.php"; call_user_func(ComposerAutoloadComposerStaticInit 832ea71bfb9a4128da8660baedaac82e::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); } }
這一部分就是對(duì)自動(dòng)加載類(lèi)的初始化,主要是給自動(dòng)加載核心類(lèi)初始化頂級(jí)命名空間映射。初始化的方法有兩種:(1)使用 autoload_static 進(jìn)行靜態(tài)初始化;(2)調(diào)用核心類(lèi)接口初始化。
autoload_static 靜態(tài)初始化靜態(tài)初始化只支持 PHP5.6 以上版本并且不支持 HHVM 虛擬機(jī)。為什么要多帶帶要求 php5.6 版本以上呢?原因就是這種靜態(tài)加載加速機(jī)制是 opcache 緩存針對(duì)靜態(tài)數(shù)組優(yōu)化的,只支持 php5.6 以上的版本。hhvm 是 php 另一個(gè)虛擬機(jī),當(dāng)然沒(méi)有辦法支持 opcache 緩存。
github相關(guān) PR: Speedup autoloading on PHP 5.6 & 7.0+ using static arrays
我們深入 autoload_static.php 這個(gè)文件發(fā)現(xiàn)這個(gè)文件定義了一個(gè)用于靜態(tài)初始化的類(lèi),名字叫 ComposerStaticInit832ea71bfb9a4128da8660baedaac82e,仍然為了避免沖突加了 hash 值和多次復(fù)用。這個(gè)類(lèi)很簡(jiǎn)單:
class ComposerStaticInit832ea71bfb9a4128da8660baedaac82e{ public static $files = array(...); public static $prefixLengthsPsr4 = array(...); public static $prefixDirsPsr4 = array(...); public static $prefixesPsr0 = array(...); public static $classMap = array (...); public static function getInitializer(ClassLoader $loader) { return Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInit832ea71bfb9a4128da8660baedaac82e::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInit832ea71bfb9a4128da8660baedaac82e::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInit832ea71bfb9a4128da8660baedaac82e::$prefixesPsr0; $loader->classMap = ComposerStaticInit832ea71bfb9a4128da8660baedaac82e::$classMap; }, null, ClassLoader::class); } }
這個(gè)靜態(tài)初始化類(lèi)的核心就是 getInitializer() 函數(shù),它將自己類(lèi)中的頂級(jí)命名空間映射給了 ClassLoader 類(lèi)。值得注意的是這個(gè)函數(shù)返回的是一個(gè)匿名函數(shù),為什么呢?原因就是 ClassLoader 類(lèi)中的 prefixLengthsPsr4、prefixDirsPsr4 等等都是 private的。。。普通的函數(shù)沒(méi)辦法給類(lèi)的 private 成員變量賦值。利用匿名函數(shù)的綁定功能就可以將把匿名函數(shù)轉(zhuǎn)為 ClassLoader 類(lèi)的成員函數(shù)。
關(guān)于匿名函數(shù)的 綁定功能。
接下來(lái)就是頂級(jí)命名空間初始化的關(guān)鍵了。
public static $classMap = array ( "AppConsoleKernel" => __DIR__ . "/../.." . "/app/Console/Kernel.php", "AppExceptionsHandler" => __DIR__ . "/../.." . "/app/Exceptions/Handler.php", "AppHttpControllersAuthForgotPasswordController" => __DIR__ . "/../.." . "/app/Http/Controllers/Auth/ForgotPasswordController.php", "AppHttpControllersAuthLoginController" => __DIR__ . "/../.." . "/app/Http/Controllers/Auth/LoginController.php", "AppHttpControllersAuthRegisterController" => __DIR__ . "/../.." . "/app/Http/Controllers/Auth/RegisterController.php", ... )
簡(jiǎn)單吧,直接命名空間全名與目錄的映射,沒(méi)有頂級(jí)命名空間。。。簡(jiǎn)單粗暴,也導(dǎo)致這個(gè)數(shù)組相當(dāng)?shù)拇蟆?/p> PSR0頂級(jí)命名空間映射:
public static $prefixesPsr0 = array ( "P" => array ( "Prophecy" => array ( 0 => __DIR__ . "/.." . "/phpspec/prophecy/src", ), "Parsedown" => array ( 0 => __DIR__ . "/.." . "/erusev/parsedown", ), ), "M" => array ( "Mockery" => array ( 0 => __DIR__ . "/.." . "/mockery/mockery/library", ), ), "J" => array ( "JakubOnderkaPhpConsoleHighlighter" => array ( 0 => __DIR__ . "/.." . "/jakub-onderka/php-console-highlighter/src", ), "JakubOnderkaPhpConsoleColor" => array ( 0 => __DIR__ . "/.." . "/jakub-onderka/php-console-color/src", ), ), "D" => array ( "DoctrineCommonInflector" => array ( 0 => __DIR__ . "/.." . "/doctrine/inflector/lib", ), ), );
為了快速找到頂級(jí)命名空間,我們這里使用命名空間第一個(gè)字母作為前綴索引。這個(gè)映射的用法比較明顯,假如我們有 Parsedown/example 這樣的命名空間,首先通過(guò)首字母 P,找到
"P" => array ( "Prophecy" => array ( 0 => __DIR__ . "/.." . "/phpspec/prophecy/src", ), "Parsedown" => array ( 0 => __DIR__ . "/.." . "/erusev/parsedown", ), )
這個(gè)數(shù)組,然后我們就會(huì)遍歷這個(gè)數(shù)組來(lái)和 Parsedown/example 比較,發(fā)現(xiàn)第一個(gè) Prophecy 不符合,第二個(gè) Parsedown 符合,然后得到了映射目錄:(映射目錄可能不止一個(gè))
array ( 0 => __DIR__ . "/.." . "/erusev/parsedown", )
我們會(huì)接著遍歷這個(gè)數(shù)組,嘗試 _DIR_ ."/.." . "/erusev/parsedown/Parsedown/example.php 是否存在,如果不存在接著遍歷數(shù)組(這個(gè)例子數(shù)組只有一個(gè)元素),如果數(shù)組遍歷完都沒(méi)有,就會(huì)加載失敗。
PSR4標(biāo)準(zhǔn)頂級(jí)命名空間映射數(shù)組:public static $prefixLengthsPsr4 = array( "p" => array ( "phpDocumentorReflection" => 25, ), "S" => array ( "SymfonyPolyfillMbstring" => 26, "SymfonyComponentYaml" => 23, "SymfonyComponentVarDumper" => 28, ... ), ... ); public static $prefixDirsPsr4 = array ( "phpDocumentorReflection" => array ( 0 => __DIR__ . "/.." . "/phpdocumentor/reflection-common/src", 1 => __DIR__ . "/.." . "/phpdocumentor/type-resolver/src", 2 => __DIR__ . "/.." . "/phpdocumentor/reflection-docblock/src", ), "SymfonyPolyfillMbstring" => array ( 0 => __DIR__ . "/.." . "/symfony/polyfill-mbstring", ), "SymfonyComponentYaml" => array ( 0 => __DIR__ . "/.." . "/symfony/yaml", ), ... )
PSR4 標(biāo)準(zhǔn)頂級(jí)命名空間映射用了兩個(gè)數(shù)組,第一個(gè)和 PSR0 一樣用命名空間第一個(gè)字母作為前綴索引,然后是頂級(jí)命名空間,但是最終并不是文件路徑,而是頂級(jí)命名空間的長(zhǎng)度。為什么呢?因?yàn)榍耙黄?文章 我們說(shuō)過(guò),PSR4 標(biāo)準(zhǔn)的文件目錄更加靈活,更加簡(jiǎn)潔。PSR0 中頂級(jí)命名空間目錄直接加到命名空間前面就可以得到路徑 (Parsedown/example => _DIR_ ."/.." . "/erusev/parsedown/Parsedown/example.php),而 PSR4 標(biāo)準(zhǔn)卻是用頂級(jí)命名空間目錄替換頂級(jí)命名空間(Parsedown/example => _DIR_ ."/.." . "/erusev/parsedown/example.php),所以獲得頂級(jí)命名空間的長(zhǎng)度很重要。
具體的用法:假如我們找 SymfonyPolyfillMbstringexample 這個(gè)命名空間,和 PSR0 一樣通過(guò)前綴索引和字符串匹配我們得到了
"SymfonyPolyfillMbstring" => 26,
這條記錄,鍵是頂級(jí)命名空間,值是命名空間的長(zhǎng)度。拿到頂級(jí)命名空間后去 $prefixDirsPsr4 數(shù)組獲取它的映射目錄數(shù)組:(注意映射目錄可能不止一條)
"SymfonyPolyfillMbstring" => array ( 0 => __DIR__ . "/.." . "/symfony/polyfill-mbstring", )
然后我們就可以將命名空間 SymfonyPolyfillMbstringexample 前26個(gè)字符替換成目錄 _DIR_ . "/.." . "/symfony/polyfill-mbstring,我們就得到了 _DIR_ . "/.." . "/symfony/polyfill-mbstring/example.php,先驗(yàn)證磁盤(pán)上這個(gè)文件是否存在,如果不存在接著遍歷。如果遍歷后沒(méi)有找到,則加載失敗。
??
自動(dòng)加載核心類(lèi) ClassLoader 的靜態(tài)初始化完成?。。?/p>
ClassLoader 接口初始化
如果PHP版本低于5.6或者使用HHVM虛擬機(jī)環(huán)境,那么就要使用核心類(lèi)的接口進(jìn)行初始化。
//PSR0標(biāo)準(zhǔn) $map = require __DIR__ . "/autoload_namespaces.php"; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } //PSR4標(biāo)準(zhǔn) $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); }PSR0 標(biāo)準(zhǔn)
autoload_namespaces:
return array( "Prophecy" => array($vendorDir . "/phpspec/prophecy/src"), "Parsedown" => array($vendorDir . "/erusev/parsedown"), "Mockery" => array($vendorDir . "/mockery/mockery/library"), "JakubOnderkaPhpConsoleHighlighter" => array($vendorDir . "/jakub-onderka/php-console-highlighter/src"), "JakubOnderkaPhpConsoleColor" => array($vendorDir . "/jakub-onderka/php-console-color/src"), "DoctrineCommonInflector" => array($vendorDir . "/doctrine/inflector/lib"), );
PSR0 標(biāo)準(zhǔn)的初始化接口:
public function set($prefix, $paths) { if (!$prefix) { $this->fallbackDirsPsr0 = (array) $paths; } else { $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths; } }
很簡(jiǎn)單,PSR0 標(biāo)準(zhǔn)取出命名空間的第一個(gè)字母作為索引,一個(gè)索引對(duì)應(yīng)多個(gè)頂級(jí)命名空間,一個(gè)頂級(jí)命名空間對(duì)應(yīng)多個(gè)目錄路徑,具體形式可以查看上面我們講的 autoload_static 的 $prefixesPsr0。如果沒(méi)有頂級(jí)命名空間,就只存儲(chǔ)一個(gè)路徑名,以便在后面嘗試加載。
PSR4標(biāo)準(zhǔn)autoload_psr4
return array( "XdgBaseDir" => array($vendorDir . "/dnoegel/php-xdg-base-dir/src"), "WebmozartAssert" => array($vendorDir . "/webmozart/assert/src"), "TijsVerkoyenCssToInlineStyles" => array($vendorDir . "/tijsverkoyen/css-to-inline-styles/src"), "Tests" => array($baseDir . "/tests"), "SymfonyPolyfillMbstring" => array($vendorDir . "/symfony/polyfill-mbstring"), ... )
PSR4 標(biāo)準(zhǔn)的初始化接口:
public function setPsr4($prefix, $paths) { if (!$prefix) { $this->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; } }
PSR4 初始化接口也很簡(jiǎn)單。如果沒(méi)有頂級(jí)命名空間,就直接保存目錄。如果有命名空間的話,要保證頂級(jí)命名空間最后是,然后分別保存(前綴=》頂級(jí)命名空間,頂級(jí)命名空間=》頂級(jí)命名空間長(zhǎng)度),(頂級(jí)命名空間=》目錄)這兩個(gè)映射數(shù)組。具體形式可以查看上面我們講的 autoload_static的prefixLengthsPsr4、 $prefixDirsPsr4。
傻瓜式命名空間映射autoload_classmap:
public static $classMap = array ( "AppConsoleKernel" => __DIR__ . "/../.." . "/app/Console/Kernel.php", "AppExceptionsHandler" => __DIR__ . "/../.." . "/app/Exceptions/Handler.php", ... )
addClassMap:
public function addClassMap(array $classMap) { if ($this->classMap) { $this->classMap = array_merge($this->classMap, $classMap); } else { $this->classMap = $classMap; } }
這個(gè)最簡(jiǎn)單,就是整個(gè)命名空間與目錄之間的映射。
結(jié)語(yǔ)其實(shí)我很想接著寫(xiě)下下去,但是這樣會(huì)造成篇幅過(guò)長(zhǎng),所以我就把自動(dòng)加載的注冊(cè)和運(yùn)行放到下一篇文章了。我們回顧一下,這篇文章主要講了:(1)框架如何啟動(dòng) composer 自動(dòng)加載;(2)composer 自動(dòng)加載分為5部分;
其實(shí)說(shuō)是5部分,真正重要的就兩部分——初始化與注冊(cè)。初始化負(fù)責(zé)頂層命名空間的目錄映射,注冊(cè)負(fù)責(zé)實(shí)現(xiàn)頂層以下的命名空間映射規(guī)則。
Written with StackEdit.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/22928.html
前言 在開(kāi)始之前,歡迎關(guān)注我自己的博客:www.leoyang90.cn上一篇 文章我們講到了 Composer 自動(dòng)加載功能的啟動(dòng)與初始化,經(jīng)過(guò)啟動(dòng)與初始化,自動(dòng)加載核心類(lèi)對(duì)象已經(jīng)獲得了頂級(jí)命名空間與相應(yīng)目錄的映射,換句話說(shuō),如果有命名空間 AppConsoleKernel,我們已經(jīng)知道了 App 對(duì)應(yīng)的目錄,接下來(lái)我們就要解決下面的就是 ConsoleKernel這一段。 注冊(cè) 我們先回顧...
摘要:今天來(lái)寫(xiě)寫(xiě)這個(gè)框架的類(lèi)加載機(jī)制版本原理在項(xiàng)目啟動(dòng)時(shí),通過(guò)注冊(cè)了要使用的類(lèi)的自動(dòng)加載處理方法,在類(lèi)第一次被使用的時(shí)候,類(lèi)文件通過(guò)該方法被引入,然后類(lèi)才得以使用源碼分析在的入口文件,我們找到我們隨著這個(gè)路徑我們找打了這個(gè)主要內(nèi)容如下其中是為了注 今天來(lái)寫(xiě)寫(xiě)Symfony2.8 這個(gè)框架的類(lèi)加載機(jī)制 版本 Symfony 2.8 原理 在項(xiàng)目啟動(dòng)時(shí),Symfony 通過(guò)spl_autoloa...
摘要:索性讀一下它的源碼。行載入類(lèi)載入類(lèi),這個(gè)類(lèi)比較重要,實(shí)現(xiàn)了自動(dòng)加載。注冊(cè)錯(cuò)誤和異常處理機(jī)制加載慣例配置文件接下來(lái)我們看一下自動(dòng)加載的實(shí)現(xiàn)方法。所以借助此函數(shù)可以達(dá)到自動(dòng)加載。博客鏈接解讀源碼一自動(dòng)加載 聽(tīng)說(shuō) TP5 已經(jīng) RC4 了,曾經(jīng)在 RC3 的時(shí)候用它寫(xiě)過(guò)一個(gè)小東西。官方說(shuō)從 RC4 以后改動(dòng)不是太大。索性讀一下它的源碼。然后順便記錄一下,如有錯(cuò)漏,請(qǐng)路過(guò)大神多多指正! 入口 ...
摘要:源碼分析自動(dòng)加載系統(tǒng)會(huì)調(diào)用方法注冊(cè)自動(dòng)加載,在這一步完成后,所有符合規(guī)范的類(lèi)庫(kù)包括依賴(lài)加載的第三方類(lèi)庫(kù)都將自動(dòng)加載。是通過(guò)加載對(duì)應(yīng)的文件進(jìn)行注冊(cè)加載的。 源碼分析 自動(dòng)加載 系統(tǒng)會(huì)調(diào)用 Loader::register()方法注冊(cè)自動(dòng)加載,在這一步完成后,所有符合規(guī)范的類(lèi)庫(kù)(包括Composer依賴(lài)加載的第三方類(lèi)庫(kù))都將自動(dòng)加載。 系統(tǒng)的自動(dòng)加載由下面主要部分組成: 1. 注冊(cè)系統(tǒng)的自...
摘要:如果遍歷后沒(méi)有找到,則加載失敗。在之后碰到了之后直接拿來(lái)用,提高系統(tǒng)自動(dòng)加載的性能。這里我們就講完了注冊(cè)自動(dòng)加載。使用自動(dòng)加載我們?cè)谥卸x了我們自動(dòng)加載函數(shù)式方法。 繼 生命周期的第二篇,大家盡可放心,不會(huì)隨便鴿文章的 第一篇中,我們提到了入口腳本,也說(shuō)了,里面注冊(cè)了自動(dòng)加載的功能 本文默認(rèn)你有自動(dòng)加載和命名空間的基礎(chǔ)。如果沒(méi)有請(qǐng) 看此篇文章 php 類(lèi)的自動(dòng)加載與命名空間 自動(dòng)加載...
閱讀 876·2021-09-26 09:55
閱讀 2158·2021-09-22 15:44
閱讀 1557·2019-08-30 15:54
閱讀 1402·2019-08-30 15:54
閱讀 2745·2019-08-29 16:57
閱讀 584·2019-08-29 16:26
閱讀 2562·2019-08-29 15:38
閱讀 2210·2019-08-26 11:48