基于PHP Tokenizer的模板引擎设计思路

PHP5以后内置了Tokenizer库, 从而可以直接使用内置的词法分析器了. 很简单,只有2个函数:

  • token_get_all 分析给定的PHP源码,并返回分析后的token数组
  • token_name 返回字符形式的token类型

token_get_all 分析的源码必须是完整的PHP语法形式,包括”“.
返回的token数组中每个元素分为2种形式, 元素为数组,则第一个元素为token的名字或者叫类型,
第二个元素是实际的值.
第二种形式就是一个字符串标量,一般是通用的操作符,分隔符等.

使用tokenizer可以实现一些高级的功能,比如,通过分析PHP源码,进行代码重写或者实现
类似java中的XDoclet. 比较实用的例子,如自动生成测试单元的代码框架.

这些都是比较正统的应用,我则考虑利用Tokenizer实现一个更为简单的模板引擎.

我们目前使用的是Smarty,Smarty的优点就是可以作为一个独立的模板语言来使用,
在我们实践中,可以使用smarty封装很多的api从而实现二次开发.

不过,Smarty的缺点也是很明显,
首先,它依赖于复杂的正则表达式来实现模板的解析.占用内存和速度都不太快,当然,这不是主要问题.
其次,由于正则表达式的先天不足,因此对于一些表达式的支持并不高.
此外,过于复杂, 相关的概念太多. 比如block,section,foreach等,其实都可以相互替代.

设计基于Tokenizer的模板引擎可以克服以上缺点,
首先,不依赖复杂的正则表达式,只需要对词法分析器分析出的词(token)进行语法分析即可.
其次,语法分析可以通过2次编译,递归降解的形式来实现,设计可以很简单.

具体实现上,我的计划:
1. 对模板源文件替换相应的标签,使PHP能够进行分析
1. PHP Tokenizer分析tokens
2. 对tokens进行简单的处理形成内部的token数组
3. 第一次编译,将token数组翻译成Runtime库可以识别的bytecode数组
4. Runtime库对bytecode进行运行执行
5. 也可以进一步优化,可以将bytecode编译成普通的PHP文件,直接运行.

其中2,3都可以通过实现PECL扩展来加速. 由于2,3的实现逻辑比较简单,因此并不难,
重点在于4, 实现一个通用的Runtime库, 设计一些标准的bytecode,
大致可以有以下几种:
[E] 执行echo命令
[M] 对象方法调用
[F 普通函数调用
[Do] Do循环
[While] While循环
[For] For 循环
[If] If 指令
[+-/*] 操作符

Runtime可以识别这些指令,然后根据指令的参数数组进行执行相应的操作.
这样,读取bytecode数组,循环,即可实现模板的运行执行.

对于指令(bytecode),开始可以实现一些最核心的,也可为编译器留有一些接口,支持bytecode的扩展接口.
不过,我怀疑是否有用,因为其实再复杂的指令其实都可以转换为几个基本bytecode的.

Comments

2 Responses to “基于PHP Tokenizer的模板引擎设计思路”

  1. 我是一条鱼 on October 10th, 2008 8:10 pm

    不会吧你要自己写模板引擎…

  2. joe.he on May 1st, 2010 2:09 pm

    token_get_all() 只是实现了解析器的词法分析部分, 重要的语法分析器,实现起来比较困难,可以考虑用antlr来处理,但是antlr本身就比较复杂,为了写一个模板引擎似乎是用牛刀了.

Leave a Reply