美文网首页
Laravel 源码解析之 composer 自动加载

Laravel 源码解析之 composer 自动加载

作者: 小拿大魔王 | 来源:发表于2017-05-06 23:01 被阅读0次

    在研究 Laravel 源码的过程中,我遇到的第一个问题就是:Laravel 的中使用到的那些类是如何被加载进来的?

    在 Laravel 的入口文件的第二行代码中,引入了 bootstrap 文件夹下的 app.php 文件。而在这个文件的开头,一个名为 Application 的类就被初始化,这个文件在此之前并未引入,程序顺利执行。

    很显然,这个自动加载的问题是在入口文件的第一行被解决的。在第一行引入的文件中,引入了 vendor 文件夹的 autoload.php 文件。这个文件与 composer 有关,Laravel 的自动加载是借助了 composer。

    composer 自动加载

    那么 composer 是如何做到自动加载的呢?

    打开 autoload.php 文件,这里面只有两行代码。第一行引入了一个文件,第二行调用了该文件中的类的 getLoader 方法。ComposerAutoloaderInit5ba1fc8bf6c79636a218962c1a6da048 这个类名之所以看起来奇怪,是因为要防止类名冲突而使用了 hash。

    <?php
    
    // autoload.php @generated by Composer
    
    require_once __DIR__ . '/composer/autoload_real.php';
    
    return ComposerAutoloaderInit5ba1fc8bf6c79636a218962c1a6da048::getLoader();
    
    

    在 getLoader 方法中,\Composer\Autoload\ClassLoader 被初始化为 $loader,然后 $loader 这个加载器的四个属性 $prefixLengthsPsr4、$prefixDirsPsr4、$prefixesPsr0、$classMap 被依次填充,前两个对应着 psr-4 规范,后面两个分别对应 psr-0 和 classMap。

        public static function getLoader()
        {
            if (null !== self::$loader) {
                return self::$loader;
            }
    
            spl_autoload_register(array('ComposerAutoloaderInit5ba1fc8bf6c79636a218962c1a6da048', 'loadClassLoader'), true, true);
            self::$loader = $loader = new \Composer\Autoload\ClassLoader();
            spl_autoload_unregister(array('ComposerAutoloaderInit5ba1fc8bf6c79636a218962c1a6da048', 'loadClassLoader'));
    
            $includePaths = require __DIR__ . '/include_paths.php';
            array_push($includePaths, get_include_path());
            set_include_path(join(PATH_SEPARATOR, $includePaths));
    
            $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\ComposerStaticInit5ba1fc8bf6c79636a218962c1a6da048::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\ComposerStaticInit5ba1fc8bf6c79636a218962c1a6da048::$files;
            } else {
                $includeFiles = require __DIR__ . '/autoload_files.php';
            }
            foreach ($includeFiles as $fileIdentifier => $file) {
                composerRequire5ba1fc8bf6c79636a218962c1a6da048($fileIdentifier, $file);
            }
    
            return $loader;
        }
    

    填充的内容来自于 vendor/composer 文件夹下的这三个文件:autoload_classmap、autoload_namespaces、autoload_psr4。打开这些文件,每一个文件都返回了一个包含了很多文件路径的数组。这些则来自于 composer.json。

        "autoload": {
            "classmap": [
                "database"
            ],
            "psr-4": {
                "App\\": "app/"
            }
        },
        "autoload-dev": {
            "classmap": [
                "tests/TestCase.php"
            ]
        },
    

    不只是 Laravel 的根目录存在 composer.json,vendor 文件夹下每一个由 composer 安装的依赖库,都存在着一个这样的文件。

    $loader 加载器被填充后,调用了它的 register 方法,在这个方法中,使用了 spl_autoload_register 注册该类的 loadClass 方法作为 __autoload 的实现。需要用到一个类的时候,就会调用 loadClass 方法。

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

    紧接着,引入了 autoload_files,与其他三个不同,它返回的数组,数组中定义的多个文件会被提前加载,而不是用到时才加载。如其中的 '/laravel/framework/src/Illuminate/Foundation/helpers.php'、'/laravel/framework/src/Illuminate/Support/helpers.php'。正是由于这两个文件被提前加载,其中的定义的框架方法才可以被在项目中任意使用。

        // vendor/laravel/framework/composer.json
        "autoload": {
            "files": [
                "src/Illuminate/Foundation/helpers.php",
                "src/Illuminate/Support/helpers.php"
            ],
            "psr-4": {
                "Illuminate\\": "src/Illuminate/"
            }
        },
    

    最后,$loader 被返回。接下来就可以在项目中没有顾忌的使用各种类和方法了,而不需要把 include 写满每一个角落。当然,前提是需要符合 psr-4 的规范。

    相关文章

      网友评论

          本文标题:Laravel 源码解析之 composer 自动加载

          本文链接:https://www.haomeiwen.com/subject/tjeytxtx.html