接上篇(1)
新建文件stupid_parser.class.php。
先定义我们的类StupidParser:
- <?php
- class StupidParser {
- }
我们这个只要一个成员变量就可以了,就是$template,他是用来保存模板的内容的。
- <?php
- class StupidParser {
- private $template;
- }
我们先写个set_file()的方法吧,他是用来设置和读取模板文件的。
- private function set_file($file) {
- $file = TPL_DIR.$file;
- if(!file_exists($file)) {
- exit('错误:模板文件不存在');
- }
- $fp = fopen($file, 'r');
- if(!$fp) exit('错误:不能打开文件');
- if(filesize($file)) {
- $this->template = fread($fp, filesize($file));
- } else {
- exit('错误:模板文件大小为零');
- }
- fclose($fp);
- return true;
- }
这个方法主要是检测模板文件存不存在和读取模板文件的内容,并把内容放置到成员变量$template中。
---------------------------------------------------------------------------------------------
好了,现在我们定义我们的模板规则吧:
1.匹配变量的模式:
{$var_name}
2.匹配条件的模式:
{if condition}.....................{/if}
3.匹配注释的模式:
{#}.......................................{#}
4.匹配包含文件的模式:
{include "file_name"}
5.匹配foreach的模式(循环数组):
{$array_name->foreach(key, value)}....................{/foreach}
6.foreach中的变量表示:
{@key} {@value}
--------------------------------------------------------------------------------------------
1.先来写个解析变量的方法吧:
- private function _parse_var() {
- $patten = "/\{\\$([a-zA-Z0-9_]+)\}/";
- if(strpos($this->template, '{$') !== false){
- $this->template = preg_replace($patten, "<?php echo \$this->_tpl_vars['$1']; ?>", $this->template);
- }
- return true;
- }
这个方法是用来匹配{$var_name}这种模式的,即匹配以大括号包含着变量的模式,我们把他替换成<?php echo $var_name; ?>这种模式。我们是用/\{\\$([a-zA-Z0-9_]+)\}/这样的正则表达式来匹配的,\{这个是匹配大括号的左边,\\$这个是匹配美元符号,([a-zA-Z0-9_]+)这个是匹配以数字字符或者下划线组成的字符串,\}这个是匹配大括号的右边。
替换完之后,我们把他存放到$this->template成员变量中。
2.接着写解析IF条件语句的方法:
- private function _parse_if() {
- if(preg_match("/\{\s*if/", $this->template)) {
- if(preg_match('/\{\s*\/if\s*\}/', $this->template)) {
- $if_patten = "/\{\s*if\s+([^}]+)\}/";
- $ef_patten = "/\{\s*\/if\s*\}/";
- $this->template = preg_replace($if_patten, "<?php if($1): ?>", $this->template);
- $this->template = preg_replace($ef_patten, "<?php endif; ?>", $this->template);
- } else {
- exit('错误:语法错误,没有封闭IF条件语句');
- }
- }
- return true;
- }
方法的开始用了if(preg_match("/\{\s*if/", $this->template))这个判断是否存在IF语句,存在我们才去编译他。/\{\s*if/这个模式是匹配{if或者{ if的形式。存在这种形式,我们就用if(preg_match('/\{\s*\/if\s*\}/', $this->template)) 判断是否存在{/if}这种形式,不存在就说明语法错误(没有封闭IF语句),我们就给个错误消息。
这两个条件都成立的话,我们就开始编译了。把{if .......}换成<?php if(......): ?>,把{/if}换成<?php endif; ?>。再保存到$this->template成员变量中。这样我们就编译完IF条件语句了~
注释语句的编译:
- private function _parse_common() {
- $patten = "/\{#\}([^{]*)\{#\}/";
- if(strpos($this->template, '{#}') !== false) {
- $this->template = preg_replace($patten, "<?php /* $1 */ ?>", $this->template);
- }
- return true;
- }
注释语句是这样({#}...............................{#})的模式,把两个{#}之间的内容注释了。所以我们先用if(strpos($this->template, '{#}') !== false) 来判断是否存在注释语句,存在我们就编译他。/\{#\}([^{]*)\{#\}/正则表达式就是用来编译{#}...............................{#}这种模式为<?php /* ....................... */ ?>的。
编译Include:
- private function _parse_include() {
- if(preg_match("/\{\s*include \"([^}]*)\"\s*\}/", $this->template, $file)) {
- if(trim($file[1]) == '') exit('错误:必须指定包含的文件');
- if(!file_exists(trim($file[1]))) exit('错误:文件不存在');
- $include_patten = '/{\s*include "([^}]+)"\s*}/';
- $this->template = preg_replace($include_patten, "<?php include '$1'; ?>", $this->template);
- }
- return true;
- }
其实include语句也很简单,我们要匹配的是{include "file_name"}这种模式,所以我们先用preg_match("/\{\s*include \"([^}]*)\"\s*\}/", $this->template, $file)来把所有的include文件都找出来,然后用if(trim($file[1]) == '')和if(!file_exists(trim($file[1]))这两句来判断文件是否存在。存在我们再去编译他。然后我们就把{include "file_name"}替换成<?php include "file_name"; ?>这种形式。再保存到$this->template成员变量中。
编译foreach语句:
- private function _parse_foreach() {
- if(preg_match("/\{\s*\\$[0-9a-zA-Z_]+\s*->\s*foreach/", $this->template)) {
- if(preg_match("/{\s*\/foreach\s*}/", $this->template)) {
- if(preg_match("/\{\s*@[0-9a-zA-Z_]+/", $this->template)) {
- $k_and_v_patten = "/\{\s*@([0-9a-zA-Z_]+)\s*\}/";
- $this->template = preg_replace($k_and_v_patten, "<?php echo \$$1; ?>", $this->template);
- }
- $foreach_patten = "/\{\s*\\$([0-9a-zA-Z_]+)\s*->\s*foreach\(\s*([0-9a-zA-Z_]+)\s*,\s*([0-9a-zA-Z_]+)\s*\)\s*\}/";
- $end_foreach_patten = "/\{\s*\/foreach\s*\}/";
- $this->template = preg_replace($foreach_patten, "<?php foreach(\$this->_tpl_vars['$1'] as \$$2 => \$$3): ?>", $this->template);
- $this->template = preg_replace($end_foreach_patten, "<?php endforeach; ?>", $this->template);
- } else {
- exit('错误:语法错误,没有封闭FOREACH条件语句');
- }
- }
- }
Foreach语句是最复杂的。
我们要匹配的是{$array_name->foreach(key, value)}....................{/foreach}这种模式,$array_name是数组名,key和value是键和值。就好像php中的foreach($array_name as $key=>$value)一样,我们就是要把前面的换成后面的形式。
我们先用if(preg_match("/\{\s*\\$[0-9a-zA-Z_]+\s*->\s*foreach/", $this->template))和if(preg_match("/{\s*\/foreach\s*}/", $this->template))来判断是否存在foreach语句和是否闭合了foreach语句。存在的话我们就用$foreach_patten和$end_foreach_patten这两个正则表达式来替换{$array_name->foreach(key, value)}这种形式为<?php foreach($array_name as $key=>$value):?>,替换{/foreach}问<?php endforeach; ?>。
我们会发现代码中有个if(preg_match("/\{\s*@[0-9a-zA-Z_]+/", $this->template))这样的判断,这个是判断是否存在{@key}这种形式的,这个是个上面的key相关的,编译之后会替换成<?php echo $key; ?>这种形式。
好了,5条语句的编译方法都写好了。
现在就写compile方法,他是调用我们刚才写的解析方法和写入编译之后的文件的:
- public function compile($file_name) {
- $this->set_file($file_name);
- $this->_parse_var();
- $this->_parse_if();
- $this->_parse_common();
- $this->_parse_foreach();
- $this->_parse_include();
- $tpl_c_file = TPL_C_DIR.md5(basename($file_name)).'.php';
- $fp = fopen($tpl_c_file, 'w');
- fwrite($fp, $this->template);
- fclose($fp);
- }
$tpl_c_file = TPL_C_DIR.md5(basename($file_name)).'.php'这句是生产编译之后的文件名。我们使用了MD5来取得一个唯一的名字。假如你觉得不好的话,可以自己改一下。最后就把解析完之后的$this->template写入文件了!
好了,这个编译类,我们就讲完了,有什么不懂可以跟帖提出。下次就说说调试类的编写,请继续支持。
最后show show全相:
- <?php
- class StupidParser {
- private $template;
- private function set_file($file) {
- $file = TPL_DIR.$file;
- if(!file_exists($file)) {
- exit('错误:模板文件不存在');
- }
- $fp = fopen($file, 'r');
- if(!$fp) exit('错误:不能打开文件');
- if(filesize($file)) {
- $this->template = fread($fp, filesize($file));
- } else {
- exit('错误:模板文件大小为零');
- }
- fclose($fp);
- return true;
- }
- public function compile($file_name) {
- $this->set_file($file_name);
- $this->_parse_var();
- $this->_parse_if();
- $this->_parse_common();
- $this->_parse_foreach();
- $this->_parse_include();
- $tpl_c_file = TPL_C_DIR.md5(basename($file_name)).'.php';
- $fp = fopen($tpl_c_file, 'w');
- fwrite($fp, $this->template);
- fclose($fp);
- }
- private function _parse_var() {
- $patten = "/\{\\$([a-zA-Z0-9_]{1,})\}/";
- if(strpos($this->template, '{$') !== false){
- $this->template = preg_replace($patten, "<?php echo \$this->_tpl_vars['$1']; ?>", $this->template);
- }
- return true;
- }
- private function _parse_if() {
- if(preg_match("/\{\s*if/", $this->template)) {
- if(preg_match('/\{\s*\/if\s*\}/', $this->template)) {
- $if_patten = "/\{\s*if\s+([^}]+)\}/";
- $ef_patten = "/\{\s*\/if\s*\}/";
- $this->template = preg_replace($if_patten, "<?php if($1): ?>", $this->template);
- $this->template = preg_replace($ef_patten, "<?php endif; ?>", $this->template);
- } else {
- exit('错误:语法错误,没有封闭IF条件语句');
- }
- }
- return true;
- }
- private function _parse_common() {
- $patten = "/\{#\}([^{]*)\{#\}/";
- if(strpos($this->template, '{#}') !== false) {
- $this->template = preg_replace($patten, "<?php /* $1 */ ?>", $this->template);
- }
- return true;
- }
- private function _parse_foreach() {
- if(preg_match("/\{\s*\\$[0-9a-zA-Z_]+\s*->\s*foreach/", $this->template)) {
- if(preg_match("/{\s*\/foreach\s*}/", $this->template)) {
- if(preg_match("/\{\s*@[0-9a-zA-Z_]+/", $this->template)) {
- $k_and_v_patten = "/\{\s*@([0-9a-zA-Z_]+)\s*\}/";
- $this->template = preg_replace($k_and_v_patten, "<?php echo \$$1; ?>", $this->template);
- }
- $foreach_patten = "/\{\s*\\$([0-9a-zA-Z_]+)\s*->\s*foreach\(\s*([0-9a-zA-Z_]+)\s*,\s*([0-9a-zA-Z_]+)\s*\)\s*\}/";
- $end_foreach_patten = "/\{\s*\/foreach\s*\}/";
- $this->template = preg_replace($foreach_patten, "<?php foreach(\$this->_tpl_vars['$1'] as \$$2 => \$$3): ?>", $this->template);
- $this->template = preg_replace($end_foreach_patten, "<?php endforeach; ?>", $this->template);
- } else {
- exit('错误:语法错误,没有封闭FOREACH条件语句');
- }
- }
- }
- private function _parse_include() {
- if(preg_match("/\{\s*include \"([^}]*)\"\s*\}/", $this->template, $file)) {
- if(trim($file[1]) == '') exit('错误:必须指定包含的文件');
- if(!file_exists(trim($file[1]))) exit('错误:文件不存在');
- $include_patten = '/{\s*include "([^}]+)"\s*}/';
- $this->template = preg_replace($include_patten, "<?php include '$1'; ?>", $this->template);
- }
- return true;
- }
- }