衆所周知composer是現代php項目的基石,composer並非一款系統級別的包管理系統,而是一個基於php項目的包依賴管理工具,它容許你聲明項目所依賴的代碼庫,它會在你的項目中安裝這些依賴。這裏咱們不講composer的具體使用細節,而是關注它自動加載方面的內容。php
composer提供了以下幾種自動加載的規範(使用PSR-4規範):json
更新autoload規則到對應的autoload配置文件使用 composer dump-autoload 命令。bash
如今這個標準已通過時了,這個標準主要考慮到了php5.2中 Code_Util_Score 這樣的寫法。若是代碼結構以下:composer
├── honey
│ └── honey
│ ├── composer.json
│ └── lib
│ └── Code
│ └── Util
│ └── Score.php
複製代碼
咱們在項目的composer.json文件裏進行autoload的聲明:函數
"autoload":{
"psr-0": {
"Code" : "vendor/honey/honey/lib/"
}
}
複製代碼
而後在項目的根目錄下執行以下命令來更新自動加載配置:工具
$ composer dump-autoload
複製代碼
而後查看vendor/composer/autoload_namespaces.phpui
// autoload_namespaces.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Code' => array($vendorDir . '/honey/honey/lib'),
);
複製代碼
能夠看到以 Code 爲前綴的類名在vendor/honey/honey/lib 目錄下尋找,在加載 Code_Util_Score 這個類時會把下劃線轉化成目錄分隔符。spa
在PSR-4裏邊須要定義一個命名空間前綴到路徑的映射(相對於包的根目錄),若是命名空間前綴Foo\指向一個文件目錄src/,當自動加載一個類時,好比Foo\Bar\Baz類,那麼這個類的路徑爲 src/Bar/Baz.php,命名空間前綴能夠不在路徑之中。在composer.json中的命名空間必須以\結尾,以免名字衝突,示例以下:code
{
"autoload": {
"psr-4": {
"Monolog\\": "src/",
"Vendor\\Namespace\\": ""
}
}
}
複製代碼
若是想把多個目錄下的文件放到同一個命名空間前綴下,能夠用以下寫法:get
{
"autoload": {
"psr-4": { "Monolog\\": ["src/", "lib/"] }
}
}
複製代碼
classmap引用的全部組合都會在install/update過程當中生成,並存儲到vendor/composer/autoload_classmap.php文件中。這個map是通過掃描指定目錄(一樣支持直接精確到文件)中全部的 .php 和 .inc 文件裏內置的類而獲得的。你能夠用classmap生成支持自定義加載的不遵循PSR-0/4規範的類庫。要配置它指向須要的目錄,以便可以準確搜索到類文件。
{
"autoload": {
"classmap": ["src/", "lib/", "Something.php"]
}
}
複製代碼
若是你想要明確的指定,在每次請求時都要載入某些文件,那麼你可使用 'files' autoloading。一般做爲函數庫的載入方式(而非類庫)。files引用的全部集合都會在install/update過程當中生成,並存儲到vendor/composer/autoload_files.php文件中。
{
"autoload": {
"files": ["src/MyLibrary/functions.php"]
}
}
複製代碼
在咱們的項目根目錄下建立一個composer.json,在這個文件裏聲明咱們的依賴:
{
"require": {
"workerman/workerman": "^3.3"
}
}
複製代碼
而後執行composer install就能夠安裝該依賴,composer會把依賴安裝到vendor目錄下並在vendor目錄生成一個autoload.php文件,在項目中引入該autoload文件就可使用vendor下面的庫了。
require_once 'vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker('http://0.0.0.0:8080');
$worker->onMessage = function($connection, $data) {
$connection->send("Hello World");
};
// 運行worker
Worker::runAll();
複製代碼
接下來咱們來看一下composer自動加載的奧祕吧。
這個文件是自動加載的入口文件,打開該文件內容以下:
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit6b02afbbb0f09c7f4f45234765981b94::getLoader();
複製代碼
能夠看到它引入了vendor/composer/autoload_real.php文件,調用了自動加載類的getLoader方法並將結果返回。
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit6b02afbbb0f09c7f4f45234765981b94 {
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;
}
spl_autoload_register(array('ComposerAutoloaderInit6b02afbbb0f09c7f4f45234765981b94', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit6b02afbbb0f09c7f4f45234765981b94', 'loadClassLoader'));
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit6b02afbbb0f09c7f4f45234765981b94::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 = Composer\Autoload\ComposerStaticInit6b02afbbb0f09c7f4f45234765981b94::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire6b02afbbb0f09c7f4f45234765981b94($fileIdentifier, $file);
}
return $loader;
}
}
function composerRequire6b02afbbb0f09c7f4f45234765981b94($fileIdentifier, $file) {
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
require $file;
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
}
}
複製代碼
下面是代碼分析:
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;
}
//註冊一個自動加載函數到__autoload函數棧中
spl_autoload_register(array('ComposerAutoloaderInit6b02afbbb0f09c7f4f45234765981b94', 'loadClassLoader'), true, true);
//實例化一個自動加載類並存儲到靜態變量裏
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
//註銷這個自動加載函數
spl_autoload_unregister(array('ComposerAutoloaderInit6b02afbbb0f09c7f4f45234765981b94', 'loadClassLoader'));
//版本判斷看使用哪些自動加載的配置文件
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
if ($useStaticLoader) {
require_once __DIR__ . '/autoload_static.php';
call_user_func(\Composer\Autoload\ComposerStaticInit6b02afbbb0f09c7f4f45234765981b94::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);
}
}
//調用加載器的註冊方法註冊自動加載函數include文件
$loader->register(true);
//加載一些函數文件
if ($useStaticLoader) {
$includeFiles = Composer\Autoload\ComposerStaticInit6b02afbbb0f09c7f4f45234765981b94::$files;
} else {
$includeFiles = require __DIR__ . '/autoload_files.php';
}
foreach ($includeFiles as $fileIdentifier => $file) {
composerRequire6b02afbbb0f09c7f4f45234765981b94($fileIdentifier, $file);
}
return $loader;
}
複製代碼
這就是composer自動加載的過程,這裏涉及到的文件以下所示:
這就是composer整個自動加載的流程。