Composer的自动加载详解

概述

众所周知composer是现代php项目的基石,composer并非一款系统级别的包管理系统,而是一个基于php项目的包依赖管理工具,它容许你声明项目所依赖的代码库,它会在你的项目中安装这些依赖。这里咱们不讲composer的具体使用细节,而是关注它自动加载方面的内容。php

自动加载类型

composer提供了以下几种自动加载的规范(使用PSR-4规范):json

  • PSR-0
  • PSR-4
  • classmap
  • files

更新autoload规则到对应的autoload配置文件使用 composer dump-autoload 命令。bash

PSR-0

如今这个标准已通过时了,这个标准主要考虑到了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

在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

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

相关文章
相关标签/搜索