用 php 实现一个视图组件和模板引擎——基础
发布于 2015-04-17 11:03

转载声明:著作权归作者所有,该内容欢迎转载,但转载时务必保留以下网址和作者信息。尊重原创,感谢有你!

作者:Chongyi 原文地址:https://www.insp.top/content/use-php-to-achieve-view-component-and-template-engine

只要不是做后端接口开发和一些作为守护进程之类的服务器脚本,大多数时候都是在和浏览器打交道,因此合理组织并展现 html 标签是最为常见的工作。一般大家使用框架时,都会自带有一套视图组件和模板引擎。

我们不讨论这些组件和引擎的好坏。因为这些东西已经经过考验,可以在生产环境下使用。我们现在只是为了学习一些东西,这时候了解一些原理上的可能对以后的帮助更大,如果一些人真的很有时间,利用这些基础知识完全可以写一个自己的组件,即可当做练习,也可以拿去自用。

好了,说这么多,我还是希望很多人明白,视图和模板引擎实际上原理十分简单,所涉及的知识可以说是太基础不过的了。但,千万不要把视图和模板引擎搞混淆,这两个真的不是一个东西 :satisfied:。

为了理解这些,我们先从视图开始了解。

## 视图

视图,你所看见的部分。

<?php echo 'hello, world';

从简单开始理解

这就是个视图文件中的代码,没错就这么简单。视图,实际上是在 MVC 这种架构上提出的。MVC 中,视图负责呈现数据。因此可以说只要是输出了数据的,都叫做视图。

在没有使用框架的时候,业务逻辑、数据的读写、组织和展示都是在一堆代码里,难以剥离,随着项目增大变得越来越难以维护。MVC 有效的分离了三者,各司其职。视图作为呈现数据的,只负责组织、展示,不再负责读写和业务逻辑。

既然视图只负责呈现数据,那么单独成一个文件,这个文件内的代码绝对不要读取数据库、做业务判断,那么就算你将视图独立出来了。这时候大多数人会想到,视图中的数据从哪来呢?

我们以一个简单的例子实现一个业务逻辑和视图分离的结构。

文件 controller.php,代码如下

<?php $time = time(); $string = ($time % 2) == 0 ? '偶数' : '奇数'; // 加载视图 include 'view.php';

文件 view.php,代码如下

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>视图实例</title> </head> <body> <p>时间戳 <?=$time?> 是 <?=$string?></p> </body> </html>

代码写好,运行后结果是预期的,对吗?

这两个文件中,毋庸置疑 view.php 就是视图文件,而 controller.php 你可以当做一个控制器,因为它实现了主要的业务逻辑(虽然极其简单)。尽管简洁,但在结构上我们已经完全分离。

事实上,目前所有的框架的视图的实现都大致如此,没什么特别的、神秘的地方,只是说框架它做了更多的事,比如验证视图文件的合法性、通过更少的参数去加载视图等等。

如何传递一个变量到视图文件?

我相信很多人在这个上面有着很多的疑惑。比如我们常看到这样向视图传递变量:

// laravel 通过视图类的 with 方法传递 View::make('view')->with('value', '实际的数据'); // Smarty 模板引擎通过 assign 方法向最终的编译好的视图传递变量 $smarty->assign('value', '实际的数据'); // ...

实际上,在上面的逻辑与视图分离的例子中已经说了。当 include 或 require 一个文件时,该文件会继承引入他的那段代码的作用域。php 官方文档是这么说的:

当一个文件被包含时,其中所包含的代码继承了 include 所在行的变量范围。从该处开始,调用文件在该行处可用的任何变量在被调用的文件中也都可用。不过所有在包含文件中定义的函数和类都具有全局作用域。

如果 include 出现于调用文件中的一个函数里,则被调用的文件中所包含的所有代码将表现得如同它们是在该函数内部定义的一样。所以它将遵循该函数的变量范围。此规则的一个例外是魔术常量,它们是在发生包含之前就已被解析器处理的。

实战 —— 实现一个视图类

假设我们有一个视图类,定义在文件 View.php

<?php class View { protected $data = []; public function display($file) { extract($this->data); include $file; } public function assign($key, $value) { $this->data[$key] = $value; } }

我们现在有模板文件,定义在文件 Template.php:

<!DOCTYPE html> <html> <head> <title><?=$title?></title> <meta charset="utf-8"> </head> <body> <h1><?=$title?></h1> <p> <?=$content?> </p> </body> </html>

然后我们的主要业务逻辑代码如下:

<?php // 包含视图类文件 include 'View.php'; $view = new View(); $view->assign('title', '视图测试'); $view->assign('content', '这是一个视图类的示例'); $view->display('Template.php');

输出的内容大家都已经看得出来了,我就不截图了。

至此,大家应该都明白如何实现一个视图了吧?其实原理就这么简单。

模板引擎

模板引擎的诞生实际上是将视图拆分出来以后的事儿。大家看到了,上面例子中,所有模板中的变量,都是通过原生的 php 语法进行输出。假如单纯是输出内容,其实不用模板引擎也可以,但是一旦涉及到需要复用模板或者对视图文件进行模块化的拆分,实现诸如继承、布局之类的功能时,原生的 php 语法似乎需要写更为复杂的代码才能实现。

很显然,我们不应该在视图文件内写超出职责或过于繁杂的额外代码,因此,模板引擎就显得十分必要。

模板引擎的用处并不是所谓的方便前端的美工和网页设计师,因为原生的语法比很多模板引擎语法更为简洁。实际上,模板引擎最主要的任务是简化在视图上的额外代码,比如处理布局、模板继承等等。

说了那么多,如何实现模板引擎?

其实模板引擎就是将一个指定的模板标记通过正则匹配,替换成相应的合法 php 语句而已。

事情太多,晚点更

灵感 INSPIRER