众所周知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整个自动加载的流程。