Laravel 视图模块运行流程

提示:

vendor\laravel\framework\src\Illuminate\View是视图模块所在的文件夹,如未说明类所在文件位置则指此文件夹。

1. 服务提供者类注入相关类到容器


在使用视图相关的功能之前是需要做些准备的,准备自然是在服务提供者类运行的(在app\config.php的providers中定义了应用中的服务提供者类),从中我们可以看出Illuminate\View\ViewServiceProvider::class指的就是ViewServiceProvider类,因此在ViewServiceProvider类中会注册一些需要用到的依赖到容器,注入名称和实例如下:

view.engine.resolver  EngineResolver
    php               PhpEngine
    blade             CompilerEngine
blade.compiler        BladeCompiler
view.finder           FileViewFinder
view                  Factory

这些类的主要关系就不一一列出了,直接看图。
file

这个时候我们已经可以看出Factory是视图模块重要的类了,因为Laravel会从容器中取出view.engine.resolver, view.finder, events(这个类是其他地方注入到容器中的)中的对象作为参数传递给Factory,而php和blade虽然并没有注入到容器,却是作为EngineResolver的属性$resolvers的值的。

2.开始使用视图


上一步骤只是做了些视图运行前的准备,而并没有真的运行视图的功能,一般运行是从view()开始的,这是一个全局的方法,这个方法是定义在vendor\laravel\framework\src\Illuminate\Foundation\helpers.php中的,这里其实调用了Factorymake(),这个方法其实主要是对传入的参数做处理并用这些参数初始化View类,再返回View的实例。

$this->callCreator($view = new View($this, $this->getEngineFromPath($path), $view, $path, $data));
return $view;

View的处理还涉及到RouterResponse的处理,不理会这个我们是知道视图其实返回的应该是字符串的才对。对象转字符串自然使用的是魔术方法__toString(),这里并不直接返回字符串,而是通过一系列的调用最终是在getContents()中返回$this->engine->get(),这里的engine是CompilerEngine,即上面View实例化的时候传递的第二个参数(也是EngineInterface的实现),因为Laravel的视图文件的名称包含blade,所以实例化的是CompilerEngine。事实上这和容器中名为view.engine.resolver的类实例有关,具体细节可以在getEngineFromPath()中找。

3.模版的编译


CompilerEngine$compiler才是实际把视图文件转成字符串的主力。一般的$compiler的实例是BladeCompiler,这个就是把Blade语法(Blade是Laravel内置模版引擎的名字)转换成PHP语法的核心。

不管上面的步骤如何的绕来绕去,把视图文件编译成字符串才是BladeCompiler类的主场功能,这个类才是真正的苦力工作者。BladeCompiler实现了CompilerInterface接口,这个接口真正工作是compile()compile()把视图文件编译成PHP的原生语法的字符串并写入到缓存目录的文件中,而getCompiledPath()根据视图文件的路径来获取编译后的文件的路径。

编译使用token_get_all()来进行语法的解析,当碰到PHP标记为T_INLINE_HTML(T_INLINE_HTML标记其实就是code无法直接解析成PHP而把这段code当作一段嵌套的HTML,这里一般就是指Blade语法的Code)的PHP标记时用对应的方法进行语法的替换,直到最终没有PHP标记为T_INLINE_HTML为止。

无法直接编译的code分为4类,分别是扩展,语句,注释,输出,这个从$compilers中可以看出,我们可以称$compilers为内部编译器,每个内部编译器对应一个相应的解析方式,如注释编译器对应compileComments()

/**
 * Compile Blade comments into valid PHP.
 *
 * @param  string  $value
 * @return string
 */
protected function compileComments($value)
{
    $pattern = sprintf('/%s--((.|\s)*?)--%s/', $this->contentTags[0], $this->contentTags[1]);

    return preg_replace($pattern, '<?php /*$1*/ ?>', $value);
}

这里就把Blade语法的code{{-- This comment will not be present in the rendered HTML --}}变成原生PHP语法的code<?php /*This comment will not be present in the rendered HTML*/ ?>了。
从中不难看出就是把Blade语法翻译成PHP原生语法的实现。当然这是最简单的内部编译器,像语句,输出内部编译器的实现就复杂的多,但只是实现复杂,涉及到正则的解析和不同形式的语句的解析,原理还是一样的。

4 总结


Laravel在部分人看来感觉复杂是因为核心的功能是通过不同组件配合运行的,只要掌握了依赖注入,容器,以及服务提供者就能够顺藤摸瓜,找到实际运行的原理。
通过上面的分析我们基本上就可以自定义模板引擎,实现自己喜欢的模板引擎了。

本帖已被设为精华帖!
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 1
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!