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整個自動加載的流程。

相關文章
相關標籤/搜索