• 写一个属于自己的模板引擎(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

  • 相关阅读:
    win10如何将现有的桌面壁纸找出来
    js 显示网站当前访客是几位访客
    SELECT DISTINCT 取列中所有不重复的值
    mysql5.6和8.0中都没有len()函数,获取字符串长度的函数是length()
    TOP 子句用于规定要返回的记录的数目。
    sqlmap提示you haven't updated sqlmap for more than 126 days!!!
    利用代码生成a-z的所有字母的指定长度的组合字典
    生成图形验证码 将图形验证码流写到前台
    JDK历史版本
    mysql 数据库隔离级别
  • 原文地址:https://www.cnblogs.com/kuyuecs/p/1390609.html
Copyright © 2020-2023  润新知