写一个属于自己的模板引擎(2)

接上篇(1)

新建文件stupid_parser.class.php。
先定义我们的类StupidParser:

  1. <?php 
  2. class StupidParser 

我们这个只要一个成员变量就可以了,就是$template,他是用来保存模板的内容的。

  1. <?php 
  2. class StupidParser 
  3. private $template

我们先写个set_file()的方法吧,他是用来设置和读取模板文件的。

  1. private function set_file($file) { 
  2. $file TPL_DIR.$file
  3. if(!file_exists($file)) { 
  4. exit('错误:模板文件不存在'); 
  5. $fp fopen($file'r'); 
  6. if(!$fp) exit('错误:不能打开文件'); 
  7. if(filesize($file)) { 
  8. $this->template fread($fpfilesize($file)); 
  9. } else { 
  10. exit('错误:模板文件大小为零'); 
  11. fclose($fp); 
  12. 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.先来写个解析变量的方法吧:
  1. private function _parse_var() { 
  2. $patten "/\{\\$([a-zA-Z0-9_]+)\}/"
  3. if(strpos($this->template'{$') !== false){ 
  4. $this->template preg_replace($patten"<?php echo \$this->_tpl_vars['$1']; ?>"$this->template); 
  5. return true

这个方法是用来匹配{$var_name}这种模式的,即匹配以大括号包含着变量的模式,我们把他替换成<?php echo $var_name; ?>这种模式。我们是用/\{\\$([a-zA-Z0-9_]+)\}/这样的正则表达式来匹配的,\{这个是匹配大括号的左边,\\$这个是匹配美元符号,([a-zA-Z0-9_]+)这个是匹配以数字字符或者下划线组成的字符串,\}这个是匹配大括号的右边。
替换完之后,我们把他存放到$this->template成员变量中。

2.接着写解析IF条件语句的方法:

  1. private function _parse_if() { 
  2. if(preg_match("/\{\s*if/"$this->template)) { 
  3. if(preg_match('/\{\s*\/if\s*\}/'$this->template)) { 
  4. $if_patten "/\{\s*if\s+([^}]+)\}/"
  5. $ef_patten "/\{\s*\/if\s*\}/"
  6. $this->template preg_replace($if_patten"<?php if($1): ?>"$this->template); 
  7. $this->template preg_replace($ef_patten"<?php endif; ?>"$this->template); 
  8. } else { 
  9. exit('错误:语法错误,没有封闭IF条件语句'); 
  10. 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条件语句了~

注释语句的编译:

  1. private function _parse_common() { 
  2. $patten "/\{#\}([^{]*)\{#\}/"
  3. if(strpos($this->template'{#}') !== false) { 
  4. $this->template preg_replace($patten"<?php /* $1 */ ?>"$this->template); 
  5. return true

注释语句是这样({#}...............................{#})的模式,把两个{#}之间的内容注释了。所以我们先用if(strpos($this->template, '{#}') !== false) 来判断是否存在注释语句,存在我们就编译他。/\{#\}([^{]*)\{#\}/正则表达式就是用来编译{#}...............................{#}这种模式为<?php /* ....................... */ ?>的。

编译Include:

  1. private function _parse_include() { 
  2. if(preg_match("/\{\s*include \"([^}]*)\"\s*\}/"$this->template$file)) { 
  3. if(trim($file[1]) == '') exit('错误:必须指定包含的文件'); 
  4. if(!file_exists(trim($file[1]))) exit('错误:文件不存在'); 
  5. $include_patten '/{\s*include "([^}]+)"\s*}/'
  6. $this->template preg_replace($include_patten"<?php include '$1'; ?>"$this->template); 
  7. 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语句:

  1. private function _parse_foreach() { 
  2. if(preg_match("/\{\s*\\$[0-9a-zA-Z_]+\s*->\s*foreach/"$this->template)) { 
  3. if(preg_match("/{\s*\/foreach\s*}/"$this->template)) { 
  4. if(preg_match("/\{\s*@[0-9a-zA-Z_]+/"$this->template)) { 
  5. $k_and_v_patten "/\{\s*@([0-9a-zA-Z_]+)\s*\}/"
  6. $this->template preg_replace($k_and_v_patten"<?php echo \$$1; ?>"$this->template); 
  7. $foreach_patten "/\{\s*\\$([0-9a-zA-Z_]+)\s*->\s*foreach\(\s*([0-9a-zA-Z_]+)\s*,\s*([0-9a-zA-Z_]+)\s*\)\s*\}/"
  8. $end_foreach_patten "/\{\s*\/foreach\s*\}/"
  9. $this->template preg_replace($foreach_patten"<?php foreach(\$this->_tpl_vars['$1'] as \$$2 => \$$3): ?>"$this->template); 
  10. $this->template preg_replace($end_foreach_patten"<?php endforeach; ?>"$this->template); 
  11. } else { 
  12. 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方法,他是调用我们刚才写的解析方法和写入编译之后的文件的:

  1. public function compile($file_name) { 
  2. $this->set_file($file_name); 
  3. $this->_parse_var(); 
  4. $this->_parse_if(); 
  5. $this->_parse_common(); 
  6. $this->_parse_foreach(); 
  7. $this->_parse_include(); 
  8. $tpl_c_file TPL_C_DIR.md5(basename($file_name)).'.php'
  9. $fp fopen($tpl_c_file'w'); 
  10. fwrite($fp$this->template); 
  11. fclose($fp); 

$tpl_c_file = TPL_C_DIR.md5(basename($file_name)).'.php'这句是生产编译之后的文件名。我们使用了MD5来取得一个唯一的名字。假如你觉得不好的话,可以自己改一下。最后就把解析完之后的$this->template写入文件了!

好了,这个编译类,我们就讲完了,有什么不懂可以跟帖提出。下次就说说调试类的编写,请继续支持。

最后show show全相:
  1. <?php 
  2. class StupidParser 
  3. private $template
  4. private function set_file($file) { 
  5. $file TPL_DIR.$file
  6. if(!file_exists($file)) { 
  7. exit('错误:模板文件不存在'); 
  8. $fp fopen($file'r'); 
  9. if(!$fp) exit('错误:不能打开文件'); 
  10. if(filesize($file)) { 
  11. $this->template fread($fpfilesize($file)); 
  12. } else { 
  13. exit('错误:模板文件大小为零'); 
  14. fclose($fp); 
  15. return true
  16. public function compile($file_name) { 
  17. $this->set_file($file_name); 
  18. $this->_parse_var(); 
  19. $this->_parse_if(); 
  20. $this->_parse_common(); 
  21. $this->_parse_foreach(); 
  22. $this->_parse_include(); 
  23. $tpl_c_file TPL_C_DIR.md5(basename($file_name)).'.php'
  24. $fp fopen($tpl_c_file'w'); 
  25. fwrite($fp$this->template); 
  26. fclose($fp); 
  27. private function _parse_var() { 
  28. $patten "/\{\\$([a-zA-Z0-9_]{1,})\}/"
  29. if(strpos($this->template'{$') !== false){ 
  30. $this->template preg_replace($patten"<?php echo \$this->_tpl_vars['$1']; ?>"$this->template); 
  31. return true
  32. private function _parse_if() { 
  33. if(preg_match("/\{\s*if/"$this->template)) { 
  34. if(preg_match('/\{\s*\/if\s*\}/'$this->template)) { 
  35. $if_patten "/\{\s*if\s+([^}]+)\}/"
  36. $ef_patten "/\{\s*\/if\s*\}/"
  37. $this->template preg_replace($if_patten"<?php if($1): ?>"$this->template); 
  38. $this->template preg_replace($ef_patten"<?php endif; ?>"$this->template); 
  39. } else { 
  40. exit('错误:语法错误,没有封闭IF条件语句'); 
  41. return true
  42. private function _parse_common() { 
  43. $patten "/\{#\}([^{]*)\{#\}/"
  44. if(strpos($this->template'{#}') !== false) { 
  45. $this->template preg_replace($patten"<?php /* $1 */ ?>"$this->template); 
  46. return true
  47. private function _parse_foreach() { 
  48. if(preg_match("/\{\s*\\$[0-9a-zA-Z_]+\s*->\s*foreach/"$this->template)) { 
  49. if(preg_match("/{\s*\/foreach\s*}/"$this->template)) { 
  50. if(preg_match("/\{\s*@[0-9a-zA-Z_]+/"$this->template)) { 
  51. $k_and_v_patten "/\{\s*@([0-9a-zA-Z_]+)\s*\}/"
  52. $this->template preg_replace($k_and_v_patten"<?php echo \$$1; ?>"$this->template); 
  53. $foreach_patten "/\{\s*\\$([0-9a-zA-Z_]+)\s*->\s*foreach\(\s*([0-9a-zA-Z_]+)\s*,\s*([0-9a-zA-Z_]+)\s*\)\s*\}/"
  54. $end_foreach_patten "/\{\s*\/foreach\s*\}/"
  55. $this->template preg_replace($foreach_patten"<?php foreach(\$this->_tpl_vars['$1'] as \$$2 => \$$3): ?>"$this->template); 
  56. $this->template preg_replace($end_foreach_patten"<?php endforeach; ?>"$this->template); 
  57. } else { 
  58. exit('错误:语法错误,没有封闭FOREACH条件语句'); 
  59. private function _parse_include() { 
  60. if(preg_match("/\{\s*include \"([^}]*)\"\s*\}/"$this->template$file)) { 
  61. if(trim($file[1]) == '') exit('错误:必须指定包含的文件'); 
  62. if(!file_exists(trim($file[1]))) exit('错误:文件不存在'); 
  63. $include_patten '/{\s*include "([^}]+)"\s*}/'
  64. $this->template preg_replace($include_patten"<?php include '$1'; ?>"$this->template); 
  65. return true

posted @ 2009-02-14 15:32  酷越  阅读(332)  评论(0编辑  收藏  举报