compoesr 解析流程

myAdream發表於2020-05-16

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 協議》,轉載必須註明作者和本文連結

相關文章