composer--------psr4加载流程解析

前言

平时工作中,一直在使用composer解决一些包依赖管理,自动加载等业务场景,但是一直没有好好看过vendor/composer目录下面的文件,今天看了下源码,也算清楚了内部的文件执行流程。

主要文件:

    vendor/autoload.php    入口文件

    vendor/composer/autoload_real.php    真正加载文件

   vendor/composer/ClassLoader.php       内部加载器文件

    vendor/composer/autoload_static.php   当php版本大于等于5.6时,内部加载器会用此文件里面的配置映射信息填充相关数组

   vendor/composer/autoload_classmap.php   当php版本小于5.6时,内部加载器会用此文件里面的配置映射信息填充加载器的classMap数组

   vendor/composer/autoload_files.php   当php版本小于5.6时,真正加载文件会在内部直接require进autoload_files文件里面的所有文件

   vendor/composer/autoload_psr4.php   当php版本小于5.6时,包含符合psr4标准的所有    命名空间:对应查找路径 的映射信息

    

psr4标准自动加载案例分析:

1、入口文件,我使用了Think\wahaha\Chu这个测试类

<?php
require './composer/vendor/autoload.php';
use Think\wahaha\Chu;

echo Chu::getName();

2、composer.json中的psr4配置信息

"autoload": {
   "psr-4": {                    
       "Think\\": "think/"
   }
}

3、目录结构

think

    wahaha

        Chu.php    

vendor

        autoload.php

        composer

            ClassLoader.php

            .......

4、自动加载类使用时,内部文件执行过程分析

  • 入口文件加载autoload.php,而autoload.php该文件引入autoload_real.php,执行了autoload_real.php文件中类的getLoader()静态方法
    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;
        }
        
        //注册类本身的loadClassLoader方法为自动加载方法
        spl_autoload_register(array('ComposerAutoloaderInitf4cf8dfb98c23a8977f6da4c2c099d38', 'loadClassLoader'), true, true);
        //实例化类加载器
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        spl_autoload_unregister(array('ComposerAutoloaderInitf4cf8dfb98c23a8977f6da4c2c099d38', 'loadClassLoader'));
        //判断运行环境信息
        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
        
        if ($useStaticLoader) {//php大于5.6时
            /*
            加载classmap,files,psr4相关映射配置文件
            类加载器相关数组配置信息初始化
            prefixLengthsPsr4、classMa、prefixDirsPsr4
            */
            require_once __DIR__ . '/autoload_static.php';
//调用返回的回调函数对象,其中回调函数中进行类加载器实例的一些相关数组配置信息初始化
call_user_func(\Composer\Autoload\ComposerStaticInitf4cf8dfb98c23a8977f6da4c2c099d38::getInitializer($loader)); } else {//php小于5.6时 //加载命名空间与查找路径映射配置信息文件,并将类加载器相关数组配置初始化 $map = require __DIR__ . '/autoload_namespaces.php'; foreach ($map as $namespace => $path) { $loader->set($namespace, $path); } //加载psr4标准的命名空间与查找路径映射配置信息文件,并将类加载器相关数组配置初始化 $map = require __DIR__ . '/autoload_psr4.php'; foreach ($map as $namespace => $path) { $loader->setPsr4($namespace, $path); } //加载ClassMap映射配置信息文件,并将类加载器相关数组配置初始化 $classMap = require __DIR__ . '/autoload_classmap.php'; if ($classMap) { $loader->addClassMap($classMap); } } //注册类加载器实例的loadClass方法为自动加载方法,实现类的自动加载 $loader->register(true); //获取files映射信息 if ($useStaticLoader) { $includeFiles = Composer\Autoload\ComposerStaticInitf4cf8dfb98c23a8977f6da4c2c099d38::$files; } else { $includeFiles = require __DIR__ . '/autoload_files.php'; } //加载files数组中的相关文件 foreach ($includeFiles as $fileIdentifier => $file) { composerRequiref4cf8dfb98c23a8977f6da4c2c099d38($fileIdentifier, $file); } return $loader; }
  • 看看类加载器ClassLoader.php文件中的loadclass方法里做了什么

    

public function loadClass($class)
{
        //查找类名对应的实际文件地址,并引入该文件
        if ($file = $this->findFile($class)) {
            includeFile($file);

            return true;
        }
}

public function findFile($class)
{
    // 查找类是否存在classMap数组中,如果在就返回类的文件路径
        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;
            }
        }
        
        //psr4标准查找
        $file = $this->findFileWithExtension($class, '.php');
       ..............
}

private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;//将类名拼接上文件扩展名  Think\wahaha\Chu.php   

        $first = $class[0];//获取类名的首字母   T
        if (isset($this->prefixLengthsPsr4[$first])) {//通过首字符查找对应的项    例如:[T=>['Think\\'=>6]],详情可查看autoload_static.php文件
            $subPath = $class;
            while (false !== $lastPos = strrpos($subPath, '\\')) {//将类名拆分,例如  Think\wahaha\Chu =>    Think\wahaha、Think
                $subPath = substr($subPath, 0, $lastPos);
                $search = $subPath.'\\';                    //    Think\    Think\wahaha\
                if (isset($this->prefixDirsPsr4[$search])) { //通过search项查找对应的目录数组 例如: ['Think\\'=>[ 0 => __DIR__ . '/../..' . '/think']],,详情可查看autoload_static.php文件
                    foreach ($this->prefixDirsPsr4[$search] as $dir) {//循环遍历目录数组
                        $length = $this->prefixLengthsPsr4[$first][$search];//获取命名空间字符长度 例如 Think\  长度为6
/*
拼接路径:
__DIR__ . '/../..' . '/think' / wahaha\Chu.php
*/
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) { return $file; } } } } } .......................... }

 

 结语:

    至此,psr4分析差不多就完了,更多细节可以自己参考里面的代码,源码中使用了匿名函数类(autoload_static.php文件中),导致刚开始有点懵,由于自己平时这块使用的比较少,所以需要再这块进行加强,平时看了一些事件机制实现,大部分都是观察者模式进行绑定和通知的。

 

posted @ 2018-04-13 17:29  rcj_飞翔  阅读(1277)  评论(0编辑  收藏  举报