Composer自动加载的实现机制

文章目录

      • 前言
          • 一些需要了解的函数
          • 文件加载
      • 自动加载的实现
          • autoload.php
          • autoload_real.php
      • 后记

前言

首先Composer官网抄的一段话,说明composer的作用:composer是 PHP 用来管理依赖(dependency)关系的工具。你可以在自己的项目中声明所依赖的外部工具库(libraries),composer会帮你安装这些依赖的库文件。

一些需要了解的函数
  • spl_autoload_register

spl_autoload_register是注册一个自动加载类的函数的方法,在没有找到某个类的时候,会调用这个函数注册的方法,去查找这个类,和__autoload类似

  • acpu_add、acpu_fetch、acpu_delete

共享内存的缓存操作

文件加载

composer的自动加载里面有5个文件autoload_static.phpautoload_classmap.phpautoload_namespace.phpautoload_psr4.phpautoload_file.php;其中autoload_static.php中的内容,是其余4个文件的总和
下面是4个文件转化后的数组形式(autoload_static.php中的内容)

  • classmap

直接把代码中use的内容作为keyclassMap数组中获取文件

public static $classMap = array (
    'ArithmeticError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/ArithmeticError.php',
    'AssertionError' => __DIR__ . '/..' . '/symfony/polyfill-php70/Resources/stubs/AssertionError.php',
    'CorsKernel' => __DIR__ . '/..' . '/barryvdh/laravel-cors/tests/CorsKernel.php',
    ...
);
  • files

直接require的文件

public static $files = array (
   '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php',
   '5255c38a0faeba867671b61dfda6d864' => __DIR__ . '/..' . '/paragonie/random_compat/lib/random.php',
   '72579e7bd17821bb1321b87411366eae' => __DIR__ . '/..' . '/illuminate/support/helpers.php',
   ...
);
  • namespace

根据psr-0命名空间规范加载的文件,取首字母作为key

public static $prefixesPsr0 = array (
    'S' => 
    array (
        'SimpleSoftwareIO\\QrCode\\' => 
        array (
            0 => __DIR__ . '/..' . '/simplesoftwareio/simple-qrcode/src',
        ),
        'Sentry\\SentryLaravel\\' => 
        array (
            0 => __DIR__ . '/..' . '/sentry/sentry-laravel/src',
        ),
    ),
    'R' => 
    array (
        'Raven_' => 
        array (
            0 => __DIR__ . '/..' . '/sentry/sentry/lib',
        ),
    ),
    ...
);
  • psr-4

根据psr-4命名空间规范,加载文件

//首字母形式
public static $prefixLengthsPsr4 = array (
    'p' => 
    array (
        'phpDocumentor\\Reflection\\' => 25,
    ),
    'W' => 
    array (
        'Webmozart\\Assert\\' => 17,
    ),
    ...
);
//目录形式
public static $prefixDirsPsr4 = array (
    'phpDocumentor\\Reflection\\' => 
    array (
        0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',
        1 => __DIR__ . '/..' . '/phpdocumentor/reflection-docblock/src',
        2 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',
    ),
    'Webmozart\\Assert\\' => 
    array (
        0 => __DIR__ . '/..' . '/webmozart/assert/src',
    ),
);

自动加载的实现

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

return ComposerAutoloaderInit5d22fa3d13ab396205f00741b2362c09::getLoader();
autoload_real.php

ComposerAutoloaderInitxxxxxxxxxxxxxx... 类,只有一个属性$loader,并通过方法getLoader()单例获取这个属性

public static function loadClassLoader($class)
{
     
    if ('Composer\Autoload\ClassLoader' === $class) {
     
        require __DIR__ . '/ClassLoader.php';
    }
}

public static function getLoader()
{
     
    //...
 	spl_autoload_register(array('ComposerAutoloaderInit5d22fa3d13ab396205f00741b2362c09', 'loadClassLoader'), true, true);
    self::$loader = $loader = new \Composer\Autoload\ClassLoader();
    spl_autoload_unregister(array('ComposerAutoloaderInit5d22fa3d13ab396205f00741b2362c09', 'loadClassLoader'));

	//php的版本大于5.6 而且 未定义HHVM_VERSION这个属性 而且 (不存在 zend_loader_file_encoded 这个方法 或者 这个方法返回值为 false)
    $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
    if ($useStaticLoader) {
     
        require_once __DIR__ . '/autoload_static.php';

        call_user_func(\Composer\Autoload\ComposerStaticInit5d22fa3d13ab396205f00741b2362c09::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\ComposerStaticInit5d22fa3d13ab396205f00741b2362c09::$files;
    } else {
     
        $includeFiles = require __DIR__ . '/autoload_files.php';
    }
    foreach ($includeFiles as $fileIdentifier => $file) {
     
        composerRequire5d22fa3d13ab396205f00741b2362c09($fileIdentifier, $file);
    }

    return $loader;
}

这里有个判断,是否是使用静态加载文件里面的内容、如果是则require静态文件autoload_static.php,并调用getInitializer方法给loader赋值,否则使用autoload_namespaces.phpautoload_psr4.phpautoload_classmap.php这几个文件赋值(其实这几个的配置的内容都是一样的,只是把文件区分了出来,应该是兼容以前的东西吧)
后面继续加载了ComposerStaticInitxxxxxxxx...::$files或者autoload_files.php的内容,并require这些文件,并在$GLOBALS['__composer_autoload_files']数组中设置标记

在前面两个操作中调用函数spl_autoload_register注册了自动加载类的方法ClassLoader::loadClass,查找类文件,并且包含这个类文件

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

        return true;
    }
}

下面我们看看如何查找类文件

public function findFile($class)
{
     
    // class map lookup
    if (isset($this->classMap[$class])) {
     
        return $this->classMap[$class];
    }
    if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
     
        return false;
    }
    if (null !== $this->apcuPrefix) {
     
        $file = apcu_fetch($this->apcuPrefix.$class, $hit);
        if ($hit) {
     
            return $file;
        }
    }

    $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');
    }

    if (null !== $this->apcuPrefix) {
     
        apcu_add($this->apcuPrefix.$class, $file);
    }

    if (false === $file) {
     
        // Remember that this class does not exist.
        $this->missingClasses[$class] = true;
    }

    return $file;
}

首先在classMap中查找是否存在文件信息,存在则直接返回,classMap数组的形式,接着判断是否仅查找classMap或者已经确认文件不存在,不存在则直接返回false,再根据key判断是否存在共享内存apcu的缓存中,存在则返回文件

上面都不存在,则根据后缀查找文件

private function findFileWithExtension($class, $ext)
{
     
    // PSR-4 lookup
    $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;

    $first = $class[0];
    if (isset($this->prefixLengthsPsr4[$first])) {
     
        $subPath = $class;
        while (false !== $lastPos = strrpos($subPath, '\\')) {
     
            $subPath = substr($subPath, 0, $lastPos);
            $search = $subPath.'\\';
            if (isset($this->prefixDirsPsr4[$search])) {
     
                $pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
                foreach ($this->prefixDirsPsr4[$search] as $dir) {
     
                    if (file_exists($file = $dir . $pathEnd)) {
     
                        return $file;
                    }
                }
            }
        }
    }

    // PSR-4 fallback dirs
    foreach ($this->fallbackDirsPsr4 as $dir) {
     
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
     
            return $file;
        }
    }

    // PSR-0 lookup
    if (false !== $pos = strrpos($class, '\\')) {
     
        // namespaced class name
        $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
            . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
    } else {
     
        // PEAR-like class name
        $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
    }

    if (isset($this->prefixesPsr0[$first])) {
     
        foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
     
            if (0 === strpos($class, $prefix)) {
     
                foreach ($dirs as $dir) {
     
                    if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
     
                        return $file;
                    }
                }
            }
        }
    }

    // PSR-0 fallback dirs
    foreach ($this->fallbackDirsPsr0 as $dir) {
     
        if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
     
            return $file;
        }
    }

    // PSR-0 include paths.
    if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
     
        return $file;
    }

    return false;
}

首先根据psr-4的规范,在prefixDirsPsr4数组中查找命名空间,拼接文件路径,返回文件;未查询到,则根据定义的目录来查找文件(这个逻辑不是很懂,可能Composer没有这个逻辑了吧)
若还未找到,则根据psr-0的规范来查找,需要把_换成/,来查找文件

psr-0psr-4的区别在于psr-4的命名空间基本就是类的路径,psr-0是配置的文件目录再加上命名空间,才是目录
Composer自动加载的实现机制_第1张图片Composer自动加载的实现机制_第2张图片

后记

以上都是我个人的看法,有什么不对的地方,希望联系我及时改正[手动抱拳],QQ:1686731979

你可能感兴趣的:(后端,composer,autoload)