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

news/2024/7/24 11:47:50 标签: php

接上篇(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

转载于:https://www.cnblogs.com/kuyuecs/archive/2009/02/14/1390609.html


http://www.niftyadmin.cn/n/1864461.html

相关文章

有关[Http持久连接]的一切,卷给你看

上文中我的结论是&#xff1a; HTTP Keep-Alive 是在应用层对TCP连接进行滑动续约复用&#xff0c; 如果客户端/服务器稳定续约&#xff0c;就成了名副其实的长连接。目前所有的Http网络库都默认开启了HTTP Keep-Alive&#xff0c;今天我们从底层TCP连接和排障角度撕碎HTTP持久…

利用ICSharpCode.SharpZipLib来实现的压缩与解压缩类

最近&#xff0c;在项目中经常需要处理压缩和解压缩文件的操作。在网上找到了相关资料&#xff0c;自己整理了下&#xff0c;写了两个类&#xff1a;一个压缩类&#xff1b;一个解压缩类。 当然是利用了ICSharpCode.SharpZipLib提供的方法来对文件压缩和解压缩。 压缩类/// <…

如何使用cURL获得请求和响应时间?

✎ 码甲说 hello&#xff0c;老伙计们&#xff0c;又有半个多月没见了&#xff0c;今天给大家分享一个干货编程小技巧&#xff0c;上至架构师、下至开发者、运维男、QA&#xff0c; 得此利器&#xff0c;事半功倍。cURL在我的眼里&#xff0c;就是一个httpClient手办&#xff…

Membership、MembershipUser和Roles类

Membership、MembershipUser和Roles类用户与角色管理在asp.net2.0中是通过Membership和Roles两个类来实现的。Membership&#xff1a;用户成员账号管理&#xff0c;用户名、密码、邮箱等Roles&#xff1a;负责用户和群组之间关系管理。l Membership类&#xff1a;主要是…

程序员的十层楼(6~7层)

作者&#xff1a; 周伟明 (14 篇文章) 日期&#xff1a; 二月 4, 2009 在 2:58 下午 第1&#xff5e;3层 看这里&#xff1a;http://software.intel.com/zh-cn/blogs/2009/02/04/1071/ 第4&#xff5e;5层 看这里&#xff1a;http://software.intel.com/zh-cn/blogs/2009/02/0…

童鞋,[HttpClient发送文件的技术实践]请查收

昨天有童鞋在群里面问&#xff1a;怎么使用HttpClient发送文件&#xff1f;01荒腔走板之前我写了一个《ABP小试牛刀之上传文件》&#xff0c;主要体现的是服务端&#xff0c;上传文件的动作是由前端小姐姐完成的&#xff0c; 我还真没有用HttpClient编程方式发送过文件。不过Ht…

浅谈MemoryCache的原生插值方式

.NET运行时内置了常用的缓存模块&#xff1a;MemoryCache标准的MemoryCache暴露了如下几个属性和方法&#xff1a;public int Count { get; } public void Compact(double percentage); public ICacheEntry CreateEntry(object key); public void Dispose(); public void Remov…

sql得到时间

sql得到当前系统时间得 日期部分 CONVERT(varchar(10),getDate(),120) 昨天 select convert(varchar(10),getdate() - 1,120) 明天 select convert(varchar(10),getdate() 1,120) 最近七天 select * from tb where 时间字段 > convert(varchar(10),getdate() - 7,120) 随后…