composer源码简单分析(一)

composer分析(一)

本文内容

  • 基于PSR-4规范的自动加载 请结合文档和下面的代码注释
  • spl_autoload_register
  • php闭包Closure简单用法(大体使用情景: 生成回调提供使用, 使用闭包绑定类中的成员属性和方法)

  1. 框架引入composer自动加载器

    require __DIR__ . '/../vendor/autoload.php';
    
  2. autoload.php文件内容

    <?php
    
    // autoload.php @generated by Composer
    
    require_once __DIR__ . '/composer/autoload_real.php';
    
    // 返回自动加载器 object(Composer\Autoload\ClassLoader)
    return ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a::getLoader();
    
  3. getLoader方法

    /**
         * @return \Composer\Autoload\ClassLoader
         */
    public static function getLoader()
    {
        // 单例
        if (null !== self::$loader) {
            return self::$loader;
        }
    
        // spl_autoload_register方法第一个参数为用户自定义的自动加载器,为一个callable,接收的参数为要加载类名
        // 如self::$loader = $loader = new \Composer\Autoload\ClassLoader(); 此脚本中并没有ClassLoader这个类,由于注册了自动加载器loadClassLoader回调,
        // 此方法就会接收到Composer\Autoload\ClassLoader这个字符串 手动require到本文件中
    
        // 第二个参数true表示注册加载器失败时抛出异常
        // 第三个参数true表示 将此自动加载器放到自动加载队列的首部,laravel中还使用此函数进行了类别名注册
        spl_autoload_register(array('ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a', 'loadClassLoader'), true, true);
        // 给loader赋值
        self::$loader = $loader = new \Composer\Autoload\ClassLoader();
        // 卸载注册的自动加载回调
        spl_autoload_unregister(array('ComposerAutoloaderInit251f259405d38ea713d5c2b4f861292a', 'loadClassLoader'));
    
        // 正常情况使用高版本php 此值恒为true
        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
        if ($useStaticLoader) {
            // require此静态加载文件 包含了大量供PSR-4使用的映射关系 后面会讲解composer包下各文件的用处
            require_once __DIR__ . '/autoload_static.php';
            // 重点在此回调的调用!!!
            // 调用返回的闭包 注册映射关系到loader中
            call_user_func(\Composer\Autoload\ComposerStaticInit251f259405d38ea713d5c2b4f861292a::getInitializer($loader));
        } else {
            // 如果$useStaticLoader为false 调用loader的set方法 手动注册映射关系到loader中 供后续加载时直接查找require文件
            $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中存在映射关系后 注册真正的自动加载器 loadClass方法
        $loader->register(true);
    
        if ($useStaticLoader) {
            // 找到composer要自动加载的全局函数文件
            $includeFiles = Composer\Autoload\ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$files;
        } else {
            // 依然是找到composer提供的全局函数 和上面的功能一致
            $includeFiles = require __DIR__ . '/autoload_files.php';
        }
        foreach ($includeFiles as $fileIdentifier => $file) {
            composerRequire251f259405d38ea713d5c2b4f861292a($fileIdentifier, $file);
        }
    
        return $loader;
    }
    
  4. autoload_static.php

    public static function getInitializer(ClassLoader $loader)
    {   
        // Closure bind方法返回的是一个闭包
        return \Closure::bind(function () use ($loader) {
            // 绑定映射关系到loader中
            $loader->prefixLengthsPsr4 = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$prefixLengthsPsr4;
            $loader->prefixDirsPsr4 = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$prefixDirsPsr4;
            $loader->prefixesPsr0 = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$prefixesPsr0;
            $loader->classMap = ComposerStaticInit251f259405d38ea713d5c2b4f861292a::$classMap;
    
        }, null, ClassLoader::class);
    }
    
  5. ClassLoader.php

    /**
         * Registers this instance as an autoloader.
         *
         * @param bool $prepend Whether to prepend the autoloader or not
         */
    public function register($prepend = false)
    {
        spl_autoload_register(array($this, 'loadClass'), true, $prepend);
    }
    
    /**
         * Loads the given class or interface.
         *
         * @param  string    $class The name of the class
         * @return bool|null True if loaded, null otherwise
         */
    public function loadClass($class)
    {
        if ($file = $this->findFile($class)) {
            // 拿到文件绝对路径
            includeFile($file);
    
            return true;
        }
    }
    
    
    /**
     * Scope isolated include.
     *
     * Prevents access to $this/self from included files.
     */
    function includeFile($file)
    {
        // 加载文件
        include $file;
    }
    
    
    /**
         * Finds the path to the file where the class is defined.
         *
         * @param string $class The name of the class
         *
         * @return string|false The path if found, false otherwise
         */
    public function findFile($class)
    {
        // class map lookup
        // classmap中保存的是类名和其绝对路径的映射关系
        if (isset($this->classMap[$class])) {
            // var_dump($class); // Illuminate\Foundation\Application
            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;
    }
    
    
    private function findFileWithExtension($class, $ext)
    {
        // PSR-4 lookup
        // 处理文件名
        // Illuminate\Foundation\Application 假设路径映射中不存在此类名
        $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
        // 拿到文件名的第一个字母 依然去找映射关系
        $first = $class[0];
        if (isset($this->prefixLengthsPsr4[$first])) {
            $subPath = $class; // Illuminate\Foundation\Application
            // 通过此步骤拿到类名的所有层级的命名空间  Illuminate\Foundation
            while (false !== $lastPos = strrpos($subPath, '\\')) {
                $subPath = substr($subPath, 0, $lastPos); // Illuminate\Foundation
                $search = $subPath . '\\';
                // prefixDirsPsr4顶级命名空间前缀
                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;
            }
        }
    
      	...
    
        return false;
    }
    

Closure使用简单示例

<?php

$a = function () {
    echo '123';
    var_dump($this);
};
// var_dump($a);

// $a(); 报错

$abc = function () {
    $this->privateVar = 456;
    echo $this->privateVar, PHP_EOL;
};

$cba = function () {
    echo $this->privateVar, PHP_EOL;
};

class MyClousre
{
    public $callback;
    private $privateVar = 123;

    // 在类内部可以直接使用 $this
    public function zbc(callable $callable)
    {
        // var_dump($callable);
        $this->callback = $callable->bindTo($this, __CLASS__); // bindTo的作用是 将闭包绑定到指定的类 打通内部状态 是闭包中可以访问类的成员
        // var_dump($callable);
    }

    public function zbcc()
    {
        call_user_func($this->callback);
        ($this->callback)();
    }
}

$mc = new MyClousre();
$mc->zbc($abc);
// $mc->zbc([new Test(), 'aaa']); // 测试结果是 bindTo只能是用在Closure对象上
$mc->zbcc();

// class Test
// {
//     public static function aaa()
//     {
//         echo $this->privateVar, PHP_EOL;
//     }
// }


// 可以达到的效果 在闭包中使用对象中的各种成员
// 在类的外部 不能直接绑定$this 要先new  
$cba = $cba->bindTo(new MyClousre(), MyClousre::class);
$cba();


// bind的使用
$foo = function () {
    echo $this->zbc() . PHP_EOL;
};

class Haiyoushui
{
    private function zbc()
    {
        return 'bayehelie';
    }
}

$bar = Closure::bind($foo, new Haiyoushui(), Haiyoushui::class);
$bar();


发现错误欢迎指正,感谢~~

下期预告:composer下的各个文件内容的生成和意义及PSR-4示例讲解

posted @ 2020-07-19 20:50  alwayslinger  阅读(205)  评论(0编辑  收藏  举报