說明:本文主要以Laravel的容器類Container為例做簡單說明Composer的自動加載機制。
Composer的自動加載機制
1、初始化一個composer項目
在一個空目錄下composer安裝Laravel的容器Container包:
composer require illuminate/container
然后在該目錄下新建一個index.php文件,然后分析下Container類為何能被實例化:
<?php /** * Created by PhpStorm. * User: liuxiang * Date: 16/5/12 * Time: 19:59 */ require_once __DIR__.'/vendor/autoload.php'; $container = new Illuminate\Container\Container(); var_dump($container);
2、分析下composer加載類的流程
使用composer最大的好處是只需最開始require一個autoload.php文件,就可以new你所需要的類了,不再需要傳統的方式A文件內各種include跳到B文件又各種include,非常頭疼。
Composer按照四種規范來加載文件:
- psr-4
- psr-0(這種規范某些部分不是很優雅)
- classmap(命名空間和文件路徑的映射)
- files
ComposerAutoloaderInit88609474169d8656473fa0223c682a7a這個類是composer為了防止類沖突搞了一個命名ComposerAutoloaderInit+hash,不管咋樣,require_once這個類后需要返回的是一個加載器$loader,而這個加載器經過四種規范遍歷后,由null被填充為含有各種變量值的ClassLoader對象。
如果仔細觀察autoload_classmap.php、autoload_namespaces.php、autoload_psr4.php和autoload_files(這里用了Container包是沒有這個文件的,但Laravel整個項目是有的)文件后,這些都按照對應的規范返回要么命名空間與路徑的映射,要么完整路徑與某個哈希的映射。
從上圖中能看出這個composer初始化路徑的流程,重點是ClassLoader這個類的loadClass($class)這個方法,是通過spl_autoload_register這個PHP自動加載函數來注冊到autoload函數棧中,最后返回一個$loader加載器,而這個加載器是包含一些私有變量的,由於本Container包只包含Illuminate\Container和Illuminate\Contracts,且都是psr-4規范,則私有變量$prefixLengthsPsr4和$prefixDirsPsr4就包含了命名空間路徑映射的數組值,其余私有變量就是空。
最后返回一個加載器$loader,然后需要實例化一個類時,就會根據loadClass($class)來尋找對應的文件,看下文。
Container類的實例化過程#
$loader這個加載器已經有了,而且它還是塞滿了各種私有變量,這些變量值為命名空間路徑映射或者路徑哈希映射等,當然這里只有命名空間路徑映射這種psr-4規范了。也就是說,一句require_once這個autoload.php文件后就拿到了一個飽滿的$loader,然后現在開始new一個類Container,那如何找到這個Container.php文件路徑的呢?
從第二個序列圖就可看出,首先調用ClassLoader中的loadClass()這個函數來找文件路徑,傳入的$class變量值是“Illuminate\Container\Container”這個字符串,然后又繼續調用findFile($class)函數先做classmap查找,然后進入findFileWithExtension($class,'.php')中做psr-4/psr-0查找,其實就是搜尋這些私有變量值,比如這里Container類是psr-4規范,那就去$prefixLengthsPsr4/$prefixDirsPsr4這些psr-4私有變量中查找文件絕對路徑,返回一個$file,再include下就等於這個類可以被實例化了。當然,這里Container.php文件絕對路徑被找到后,發現它還實現了一個接口ContractContainer,那就再去同樣方式找這個文件:psr-4根據命名空間Illuminate\Contracts\Container\Container去找這個接口對應的絕對路徑。
總之,當實例化一個類時,這個$loader就去根據四種規范找該文件的絕對路徑,如果這個類還有繼承或實現關系,那就遞歸找。
自定義一個類文件#
現在自己寫一個類文件,當實例化的時候,然后讓composer來自動加載,怎么做?
修改composer.json文件:
{ "require": { "illuminate/container": "^5.2" }, "autoload": { "psr-4": { "App\\": "app/" } } }
這里按照psr-4規范來,然后在項目根目錄下使用命令:
composer install
發現autoload_psr4.php文件會多一個數組值:
return array( 'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'), 'Illuminate\\Container\\' => array($vendorDir . '/illuminate/container'), 'App\\' => array($baseDir . '/app'), );
然后在項目根目錄下新建文件:
// app/Test/Test.php文件 <?php /** * Created by PhpStorm. * User: liuxiang * Date: 16/5/12 * Time: 21:52 */ namespace App\Test; class Test { public function index() { echo "This is a custom class which will be autoload by composer\n"; } }
在index.php文件中就可以實例化Test類並調用其對象函數了:
require_once __DIR__.'/vendor/autoload.php'; //$container = new Illuminate\Container\Container(); //var_dump($container); $test = new App\Test\Test(); $test->index();
終端執行輸出:
通過在Composer中注冊下,Composer就可以幫我們找到類文件,就不需要自己各種include,只需開始一句require_once就行,真的很方便。
One More Thing...
配置Xdebug。
強烈推薦在自己的IDE中配置Xdebug,作者使用PHPStorm,並配置了Xdebug,這會提高閱讀源碼的效率。具體操作流程可以谷歌文檔,應該很多,Netbeans或者ZendStudio應該也有很多配置文檔。如果有配置不成功的,可以在本文留言下問題,作者會盡量解答。
PlantUML插件的安裝。
本文UML序列圖用的是PlantUML這個插件來做的,還比較好用,推薦下。可以在PHPStorm插件庫里搜UML就行,然后新建一個文件時會發現多了好幾個UML選項,並且還有一個PlantUML窗口:
關於這個PlantUML有一篇文章還挺好:Create Beautiful UML Diagrams in Minutes from the JetBrains IDE,還有它的官網(就是有各種廣告):PlantUML。
總結:本文主要聊了下Composer的加載流程,並以Laravel的Illuminate\Container包為例具體說明實例化類時是如何找到其文件的,並講述如何自定義自己的類並通過Composer來注冊和加載。過兩天還想結合PHP的字符串和數組這些基礎知識新開篇章,到時見。