compoesr 解析流程

composer 加载文件#

autoload.php#

<?php

// autoload.php @generated by Composer

require_once __DIR__ . '/composer/autoload_real.php';

return ComposerAutoloaderInitc8e7e2ff9618f4a7666639dbd0c65a25::getLoader();

autoload.php 文件中通过 require_once 引入 autoload_real.php 文件,在调用 ComposerAutoloaderInitc8e7e2ff9618f4a7666639dbd0c65a25::getLoader(); 获取加载器

autoload_real.php#

getLoader 方法#

  • 判断是否加载器是否已经存在,加载器存在就返回
if (null !== self::$loader) {
  return self::$loader;
}
  • 通过 spl_autoload_register 调用自身 loadClassLoader 方法
spl_autoload_register(array('ComposerAutoloaderInitc8e7e2ff9618f4a7666639dbd0c65a25', 'loadClassLoader'), true, true);
  • loadClassLoader 方法中验证传递 $class 变量是否等于 Composer\Autoload\ClassLoader , 相等的话使用 require 引入 ClassLoader
public static function loadClassLoader($class)
{
  if ('Composer\Autoload\ClassLoader' === $class) {
    require __DIR__ . '/ClassLoader.php';
  }
}
  • 通过 loadClassLoader 方法中引入的 ClassLoader 类来创建加载器
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
  • 通过 spl_autoload_unregister 注销已经引入的类
spl_autoload_unregister(array('ComposerAutoloaderInitc8e7e2ff9618f4a7666639dbd0c65a25', 'loadClassLoader'));
  • 判断当前 php 版本和运许环境
PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
  1. 使用 PHP_VERSION_ID 判断当前 php 版本
  2. 使用 HHVM_VERSION 判断是否已经定义,存在代表是当前运行环境是 HHVM
  3. 判断 zend_loader_file_encoded 方法是否存在,存在调用该方法在取反
  • 根据 $useStaticLoader 状态判断
  1. $useStaticLoadertrue

引入 autoload_static.php 文件,调用 ComposerStaticInitc8e7e2ff9618f4a7666639dbd0c65a25 类中 getInitializer 静态方法获取闭包函数,在使用 call_user_func 调用闭包方法

require_once __DIR__ . '/autoload_static.php';

call_user_func(\Composer\Autoload\ComposerStaticInitc8e7e2ff9618f4a7666639dbd0c65a25::getInitializer($loader));

getInitializer 方法中注册了,命名空间 ($prefixLengthsPsr4), 命名空间路径 ($prefixDirsPsr4), $prefixesPsr0, 类文件映射 ($classMap)

public static function getInitializer(ClassLoader $loader)
{
    return \Closure::bind(function () use ($loader) {
        $loader->prefixLengthsPsr4 = ComposerStaticInitc8e7e2ff9618f4a7666639dbd0c65a25::$prefixLengthsPsr4;
        $loader->prefixDirsPsr4 = ComposerStaticInitc8e7e2ff9618f4a7666639dbd0c65a25::$prefixDirsPsr4;
        $loader->prefixesPsr0 = ComposerStaticInitc8e7e2ff9618f4a7666639dbd0c65a25::$prefixesPsr0;
        $loader->classMap = ComposerStaticInitc8e7e2ff9618f4a7666639dbd0c65a25::$classMap;

    }, null, ClassLoader::class);
}
  1. $useStaticLoaderfalse

手动引入文件和注册

$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);
}
  • 调用加载器的 register 方法
$loader->register(true);

register 方法这个放在后面说,放在这里会影响内容

  • 引入自动加载文件
if ($useStaticLoader) {
    $includeFiles = Composer\Autoload\ComposerStaticInitc8e7e2ff9618f4a7666639dbd0c65a25::$files;
} else {
    $includeFiles = require __DIR__ . '/autoload_files.php';
}

判断 $useStaticLoader 的状态来选择文件加载方式

  • 注册加载文件到 $GLOBALS 全局变量中
foreach ($includeFiles as $fileIdentifier => $file) {
    composerRequirec8e7e2ff9618f4a7666639dbd0c65a25($fileIdentifier, $file);
}

循环调用 composerRequirec8e7e2ff9618f4a7666639dbd0c65a25 方法

function composerRequirec8e7e2ff9618f4a7666639dbd0c65a25($fileIdentifier, $file)
{
    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
        require $file;

        $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
    }
}

composerRequirec8e7e2ff9618f4a7666639dbd0c65a25 方法是否已经引入,没有引入先入文件在赋值到 $GLOBALS 全局变量中

  • 最后返回加载器

ClassLoader.php#

register 方法#

使用 spl_autoload_register 调用自己 loadClass 方法加载类

public function register($prepend = false)
{
    spl_autoload_register(array($this, 'loadClass'), true, $prepend);
}

loadClass 方法#

通过 findFile 加载 $class 文件路径,通过 includeFile 方法加载文件

public function loadClass($class)
{
  if ($file = $this->findFile($class)) {
    includeFile($file);

    return true;
  }
}

includeFile 方法#

使用 include 引入文件

function includeFile($file)
{
    include $file;
}

findFile 方法#

  1. 使用 classMap 映射返回文件
// class map lookup
if (isset($this->classMap[$class])) {
  return $this->classMap[$class];
}
  1. classMapAuthoritativemissingClasses 判断是否返回 false
  • classMapAuthoritative 目前也清楚是干嘛的
  • missingClasses 判断这个是否为缺失状态
  1. apcuPrefix 不为 null, 使用 apcu_fetch 获取文件
if (null !== $this->apcuPrefix) {
  $file = apcu_fetch($this->apcuPrefix.$class, $hit);
  if ($hit) {
    return $file;
  }
}
  1. 使用 findFileWithExtension 查询文件
  • 查询 .php 文件 或者 .hh 文件
$file = $this->findFileWithExtension($class, '.php');

// Search for Hack files if we are running on HHVM
if (false === $file && defined('HHVM_VERSION')) {
  $file = $this->findFileWithExtension($class, '.hh');
}
  • 如果 apcuPrefix 不为 null, 使用 apcu_add 把文件添加到缓存中
if (null !== $this->apcuPrefix) {
  apcu_add($this->apcuPrefix.$class, $file);
}
  • 如果文件不存在,标记为缺失状态
if (false === $file) {
  // Remember that this class does not exist.
  $this->missingClasses[$class] = true;
}
本作品采用《CC 协议》,转载必须注明作者和本文链接