zoukankan      html  css  js  c++  java
  • 写一个属于自己的模板引擎(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

  • 相关阅读:
    Eclipse 远程调试
    大数据处理方法bloom filter
    sicily 1259 Sum of Consecutive Primes
    sicily 1240. Faulty Odometer
    sicily 1152 简单马周游 深度优先搜索及回溯算法
    sicily 1050 深度优先搜索解题
    sicily 1024 邻接矩阵与深度优先搜索解题
    sicily 1156 二叉树的遍历 前序遍历,递归,集合操作
    sicily 1443 队列基本操作
    sicily 1006 team rankings 枚举解题
  • 原文地址:https://www.cnblogs.com/kuyuecs/p/1390609.html
Copyright © 2011-2022 走看看