laravel源码阅读之自动加载功能

这篇文章我们来看一下laravel使用的自动加载机制,因为我们使用 composer 来管理整个项目,因此这里的自动加载机制其实是 composer 实现的。

首先,从 /public/index.php 我们可以看到,在生成框架实例前,类和函数自动加载相关代码会先被执行。

我们先看启动文件 /bootstrap/autoload.php。首先它载入了composer的自动加载文件 /vendor/autoload.php由于整个框架和第三方类库都是用 composer 进行管理的,因此使用它是十分合理的。
接下来它又载入了编译好的类文件 /bootstrap/cache/compiled.php,用来提高应用的性能

下面我们先看 autoload.php 文件,代码很简单

require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInitf::getLoader();

这是composer自动生成的一个引导文件,它载入了真正的类加载文件,然后调用了引导类的 getLoader 方法。

我们来看 autoload_real.php,文件定义了ComposerAutoloaderInit类和composerRequire方法(为了清晰起见,类和方法后面的标识id省略,部分对分析影响不大的源码也省略)。从上一个文件我们知道它调用了 ComposerAutoloaderInit 类的 getLoader 方法。我们先来分析 getLoader,代码如下,先简要看注释,了解大致流程,接下来我们再进行分析:

public static function getLoader()
{
    // 使用单例模式发挥一个 引导类实例
    if (null !== self::$loader) {
        return self::$loader;
    }

    // 注册loadClassLoader加载方法
    spl_autoload_register(array('ComposerAutoloaderInit', 'loadClassLoader'), true, true);
    // 生成引导类后,又把 loadCLassLoader 引导方法注销
    self::$loader = $loader = new \Composer\Autoload\ClassLoader();
    spl_autoload_unregister(array('ComposerAutoloaderInit', 'loadClassLoader'));

    // 如果php版本大于等于5.6
    if (PHP_VERSION_ID >= 50600) {
        require_once __DIR__ . '/autoload_static.php';
        // ComposerStaticInit 类就存在于上述 autoload_static.php 文件中
        call_user_func(\Composer\Autoload\ComposerStaticInit::getInitializer($loader));
    } else { ... }
    // 注册自动加载函数
    // 到这里就实现了框架和第三方类库的自动加载
    $loader->register(true);

    // 载入全局函数文件
    if (PHP_VERSION_ID >= 50600) {
        $includeFiles = Composer\Autoload\ComposerStaticInit::$files;
    } 
    foreach ($includeFiles as $fileIdentifier => $file) {
        composerRequire($fileIdentifier, $file);
    }

    return $loader;
}

从第十行我们看到了 loadClassLoader 加载函数,其如下

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

它的目的很简单,加载 ClassLoader.php 文件。上面注释说了,加载这个文件的唯一目的就是为了生成 loader 实例,即 \Composer\Autoload\ClassLoader()。有趣的是,如果不使用自动加载而直接载入这个文件,达到的效果是一样的,至于具体为什么这么写,个人猜测是作者习惯。
现在直接看框架实例 laoder类,在 ClassLoader 类中,它实现了 PSR-0, PSR-4 和类的自动加载。PSR-0 定义了命名空间和实际文件路径的对应关系等标准,PSR-4 是关于由文件路径自动载入对应类的相关规范,主要是要理清命 全命名空间、命名空间前缀、文件基目录、文件路径 等的关系。强烈建议阅读这个实例,它是psr-4标准的一个实现,对理解整个框架的自动加载机制有很大的帮助(实际上后面的自动加载基本是这个实例的扩展版本)
或者简单来说,这个文件定义了:
1. 类、函数与具体文件的映射方式(通过保存在关联数组来实现)
2. 将这些关系的自动加载函数注册到自动加载队列,实现自动加载。(使用 spl_autoload_register)
3. 定义了命名空间解析成具体路径的方法(psr-4的实现)
4. 定义了具体类解析成具体路径的方法

ok,现在我们只要知道上面的东西就够了,剩下的等遇到了再来看。
回到 autoload_real.php,以php5.6为例,它又加载进了 autoload_static.php 文件,并调用了 getInitializer函数。函数如下

public static function getInitializer(ClassLoader $loader)
{
    // 复制当前的闭包对象,绑定指定的this和类作用域
    // 绑定的对象决定了在匿名函数类中的$this。
    // “类作用域”代表一个类型、决定在这个匿名函数中能够调用哪些 私有 和 保护 的方法。 
    return \Closure::bind(function () use ($loader) {
        $loader->prefixLengthsPsr4 = ComposerStaticInit::$prefixLengthsPsr4;
        $loader->prefixDirsPsr4 = ComposerStaticInit::$prefixDirsPsr4;
        $loader->prefixesPsr0 = ComposerStaticInit::$prefixesPsr0;
        // 主要定义了框架外类,即vendor外的类(大部分是我们的业务类)
        $loader->classMap = ComposerStaticInit::$classMap;

    }, null, ClassLoader::class);
}

这个函数的作用本身很明显,比较复杂的是它使用了闭包的特性,上面与闭包相关的我们只要知道一点: bind的第三个参数用来设置类作用域,效果是我们可以在里面修改ClassLoader类的 proteced, private属性,也就是上面的 prefixLengthsPsr4等loader的成员属性。

可以发现,这个函数的作用仅仅是将我们前面提到 命名空间前缀和基路径 等属性进行赋值,简单来说,以后遇到诸如如 ComposerStaticInit::$prefixDirsPsr4 属性(映射数组)中定义的类路径下的类,则可以实现自动加载。查看上述几个类属性,可以发现 laravel框架以及其他第三方类库的库文件以及我们的业务类路径 都已经定义在了上述四个属性中,接下来,执行 $loader->register()对自动加载函数进行注册,程序执行到这之后,vendor中的类库以及我们自定义的类就实现了自动加载!
后面又载入了全局函数文件(如注释所示)。到这里,整个类的自动加载机制就完成了。

回到/bootstrap/index.php 文件,第一行代码require __DIR__.'/../bootstrap/autoload.php' 执行完毕后,也就实现了 框架类、第三方类和函数以及我们自定义类、函数 的自动加载!
然而悲伤的是,说了这么多,我们还没有踏进 laravel 的大门,不过知道了它现在可以自动加载这么多东西后,以后遇到相关的类文件我们也就不用着急地去探寻它们的require了,composer 已经帮我们搞定了嘛 (=:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值