概述
众所周知composer是现代php项目的基石,composer并不是一款系统级别的包管理系统,而是一个基于php项目的包依赖管理工具,它允许你声明项目所依赖的代码库,它会在你的项目中安装这些依赖。这里我们不讲composer的具体使用细节,而是关注它自动加载方面的内容。
自动加载类型
composer提供了如下几种自动加载的规范(使用PSR-4规范):
- PSR-0
- PSR-4
- classmap
- files
更新autoload规则到对应的autoload配置文件使用 composer dump-autoload 命令。
PSR-0
现在这个标准已经过时了,这个标准主要考虑到了php5.2中 Code_Util_Score 这样的写法。如果代码结构如下:
├── 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.php
// 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 这个类时会把下划线转化成目录分隔符。
PSR-4
在PSR-4里边需要定义一个命名空间前缀到路径的映射(相对于包的根目录),如果命名空间前缀Foo\指向一个文件目录src/,当自动加载一个类时,比如Foo\Bar\Baz类,那么这个类的路径为 src/Bar/Baz.php,命名空间前缀可以不在路径之中。在composer.json中的命名空间必须以\结尾,以避免名字冲突,示例如下:
{
"autoload": {
"psr-4": {
"Monolog\\": "src/",
"Vendor\\Namespace\\": ""
}
}
}
如果想把多个目录下的文件放到同一个命名空间前缀下,可以用如下写法:
{
"autoload": {
"psr-4": { "Monolog\\": ["src/", "lib/"] }
}
}
classmap
classmap引用的所有组合都会在install/update过程中生成,并存储到vendor/composer/autoload_classmap.php文件中。这个map是经过扫描指定目录(同样支持直接精确到文件)中所有的 .php 和 .inc 文件里内置的类而得到的。你可以用classmap生成支持自定义加载的不遵循PSR-0/4规范的类库。要配置它指向需要的目录,以便能够准确搜索到类文件。
{
"autoload": {
"classmap": ["src/", "lib/", "Something.php"]
}
}
files
如果你想要明确的指定,在每次请求时都要载入某些文件,那么你可以使用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自动加载的奥秘吧。
vendor/autoload.php
这个文件是自动加载的入口文件,打开该文件内容如下:
// autoload.php @generated by Composer
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit6b02afbbb0f09c7f4f45234765981b94::getLoader();
可以看到它引入了vendor/composer/autoload_real.php文件,调用了自动加载类的getLoader方法并将结果返回。
vendor/composer/autoload_real.php
// 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自动加载的过程,这里涉及到的文件如下所示:
- vendor/autoload.php 自动加载入口文件
- vendor/composer/autoload_real.php 自动加载核心文件
- vendor/composer/ClassLoader.php 自动加载类具体实现文件
- vendor/composer/autoload_static.php 所有的自动加载配置
- vendor/composer/autoload_classmap.php classmap自动加载配置
- vendor/composer/autoload_namespaces.php PSR0自动加载配置
- vendor/composer/autoload_psr4.php PSR4自动加载配置
- vendor/composer/autoload_files.php files自动加载配置
这就是composer整个自动加载的流程。