当前位置: 代码迷 >> PHP >> DolrPHP模板引擎DolrViews分享
  详细解决方案

DolrPHP模板引擎DolrViews分享

热度:55   发布时间:2016-04-29 00:18:20.0
【分享】DolrPHP模板引擎DolrViews分享
核心文件:DolrViews.class.php:
  1. <?php
  2. /**
  3. * DolrPHP模板引擎
  4. * @author Joychao <[email protected]>
  5. * @version 1.0 beta
  6. * @license http://www.Joychao.cc
  7. */
  8. defined('DOLRVIEWS') or define('DOLRVIEWS',true);
  9. defined('DIR_SEP') or define('DIR_SEP',DIRECTORY_SEPARATOR);
  10. class DolrViews
  11. {
  12. /**
  13. * 单例对象
  14. *
  15. * @static var
  16. * @var object DolrViews
  17. */
  18. protected static $_instance;
  19. /**
  20. * 模板变量
  21. *
  22. * @var array
  23. */
  24. protected $_tpl_vars=array();
  25. /**
  26. * 模板参数信息
  27. *
  28. * @var array
  29. */
  30. protected $_options=array(
  31. 'template_dir'=>'templates',
  32. 'compile_dir'=>'templates_c',
  33. 'cache_dir'=>'cache',
  34. 'caching'=>false,
  35. 'cache_lifetime'=>3600,
  36. 'plugins_dir'=>'plugins',
  37. 'tpl_suffix'=>'html',
  38. 'php_handing'=>false,
  39. );
  40. /**
  41. * 包含的插件
  42. * @var array
  43. */
  44. protected $includePlugins=array();
  45. /**
  46. * 主模板非包含模板
  47. * @var string
  48. */
  49. protected $mainTplFileName;
  50. /**
  51. * 单件模式调用方法
  52. *
  53. * @static
  54. * @return object DolrViews
  55. */
  56. public static function getInstance($options=array()) {
  57. if (!self :: $_instance instanceof self)
  58. self :: $_instance = new self($options);
  59. return self :: $_instance;
  60. }
  61. /**
  62. * 构造函数,初始化所有配置
  63. *
  64. * @param array $config=array(); 配置数组
  65. * @example:
  66. * $_options=array(
  67. * 'template_dir'=>'templates',
  68. * 'compile_dir'=>'templates_c',
  69. * 'cache_dir'=>'cache',
  70. * 'caching'=>false,
  71. * 'cache_lifetime'=>3600,
  72. * 'plugins_dir'=>'plugins',
  73. * 'php_handing'=>false,
  74. * );
  75. * $tpl=new DolrViews($_options);
  76. */
  77. public function __construct($options=array())
  78. {
  79. if (!empty($options))
  80. {
  81. $this->setOptions($options);
  82. }
  83. }
  84. /**
  85. * 分配变量到模板
  86. *
  87. * @param string $varName 变量名
  88. * @param mixed $varValue 变量值
  89. */
  90. public function assign($varName,$varValue)
  91. {
  92. $this->_tpl_vars[$varName]=&$varValue;
  93. }
  94. /**
  95. * 显示输出到页面
  96. *
  97. * @param string $tplFileName 模板文件名
  98. */
  99. public function display($tplFileName,$cacheId=null)
  100. {
  101. $cacheId=is_null($cacheId)?$_SERVER['REQUEST_URI']:$cacheId;
  102. $this->mainTplFileName=$tplFileName;
  103. $tplFilePath=$this->_getTplPath($tplFileName);
  104. extract($this->_tpl_vars);
  105. //内置变量
  106. extract($this->_getSystemVars($tplFileName));//系统变量
  107. //是否缓存
  108. if($this->_options['caching']){
  109. if($this->isCached($tplFileName,$cacheId)){
  110. include $this->_getCacheFile($tplFileName,$cacheId);
  111. }else{//否则缓存文件
  112. include $this->_writeCache($tplFileName,$this->_parseTpl($tplFileName,$cacheId),$cacheId);// 写入缓存
  113. }
  114. }else{//非缓存模式
  115. include $this->_parseTpl($tplFileName,$cacheId);//解析写入编译文件并包含
  116. }
  117. }
  118. /**
  119. * 配置模板选项
  120. *
  121. * @param array $options 配置数组
  122. */
  123. public function setOptions($options)
  124. {
  125. foreach ($options as $optionName => $optionValue) {
  126. $this->set($optionName,$optionValue);
  127. }
  128. }
  129. /**
  130. * 设置选项
  131. *
  132. * @param string $optionName 选项名称
  133. * @param mixed $optionValue 选项值
  134. */
  135. public function __set($optionName,$optionValue)
  136. {
  137. $this->set($optionName,$optionValue);
  138. }
  139. /**
  140. * 单独设置配置
  141. *
  142. * @param string $optionName 选项名称
  143. * @param mixed $optionValue 选项值
  144. */
  145. public function set($optionName,$optionValue)
  146. {
  147. switch (strtolower($optionName)) {
  148. case 'template_dir':
  149. $optionValue=$this->_trimPath($optionValue).DIR_SEP;
  150. if(!file_exists($optionValue))
  151. $this->_throwException('未找到指定的模板目录"'.$optionValue.'"');
  152. $this->_options['template_dir']=$optionValue;
  153. break;
  154. case 'compile_dir':
  155. $optionValue=$this->_trimPath($optionValue).DIR_SEP;
  156. if(!file_exists($optionValue))
  157. $this->_throwException('未找到指定的编译目录"'.$optionValue.'"');
  158. $this->_options['compile_dir']=$optionValue;
  159. break;
  160. case 'cache_dir':
  161. $optionValue=$this->_trimPath($optionValue).DIR_SEP;
  162. if(!file_exists($optionValue))
  163. $this->_throwException('未找到指定的缓存目录"'.$optionValue.'"');
  164. $this->_options['cache_dir']=$optionValue;
  165. break;
  166. case 'plugins_dir':
  167. $optionValue=$this->_trimPath($optionValue).DIR_SEP;
  168. if(!file_exists($optionValue))
  169. $this->_throwException('未找到指定的缓存目录"'.$optionValue.'"');
  170. $this->_options['plugins_dir']=$optionValue;
  171. break;
  172. case 'caching':
  173. $this->_options['caching']=(boolean)$optionValue;
  174. break;
  175. case 'cache_lifetime':
  176. $this->_options['cache_lifetime']=(float)$optionValue;
  177. break;
  178. case 'php_handing':
  179. $this->_options['php_handing']=(boolean)$optionValue;
  180. break;
  181. case 'tpl_suffix':
  182. $this->_options['tpl_suffix']=trim($optionValue);
  183. break;
  184. default:
  185. $this -> _throwException("未知的模板配置选项 \"$optionName\"");
  186. }
  187. }
  188. /**
  189. * 清除模板缓存
  190. *
  191. * @param string $tplFileName='' 模板文件名,不传入则删除所有缓存
  192. * @return boolean 成功或者失败
  193. */
  194. public function clearCache($tplFileName='',$cacheId=null)
  195. {
  196. $cacheId=is_null($cacheId)?$_SERVER['REQUEST_URI']:$cacheId;
  197. if(!empty($tplFileName)){
  198. $cacheFile=$this->_getCacheFile($tplFileName,$cacheId);
  199. if(file_exists($cacheFile)){
  200. chmod($cacheFile, 0777);
  201. @unlink($cacheFile);
  202. }
  203. }else{//删除所有缓存文件
  204. foreach (glob($this->_options['cache_dir'].'*') as $cacheFile) {
  205. chmod($cacheFile, 0777);
  206. @unlink($cacheFile);
  207. }
  208. }
  209. }
  210. /**
  211. * 检测是否缓存了指定模板文件
  212. *
  213. * @param string $tplFileName 模板文件名
  214. * @return boolean
  215. */
  216. public function isCached( $tplFileName,$cacheId=null)
  217. {
  218. $tplFilePath=$this->_getTplPath($tplFileName);
  219. $cacheId=is_null($cacheId)?$_SERVER['REQUEST_URI']:$cacheId;
  220. $cacheFile=$this->_getCacheFile($tplFileName,$cacheId);
  221. if(file_exists($cacheFile) //存在
  222. and filemtime($cacheFile)+$this->_options['cache_lifetime']>time()//未过期
  223. and filemtime($cacheFile)>filemtime($tplFilePath) //模板没有改动
  224. ){//存在并没过期
  225. return true;
  226. }else{
  227. return false;
  228. }
  229. }
  230. /**
  231. * 获取内置变量
  232. * @return array
  233. */
  234. protected function _getSystemVars($tplFileName){
  235. //内置变量
  236. $_sysVars=array();
  237. $_sysVars['dolr_now']=time();
  238. $_sysVars['dolr_get']=$_GET;
  239. $_sysVars['dolr_post']=$_POST;
  240. $_sysVars['dolr_request']=$_REQUEST;
  241. $_sysVars['dolr_cookie']=isset($_COOKIE)?$_COOKIE:null;
  242. $_sysVars['dolr_session']=isset($_SESSION)?$_SESSION:null;
  243. $_sysVars['dolr_template']=basename($tplFileName);
  244. $const=get_defined_constants(true);
  245. $_sysVars['dolr_const']=$const['user'];
  246. $_sysVars['dolr_url']="http://".$_SERVER ['HTTP_HOST'].$_SERVER['PHP_SELF'];
  247. if(!empty($_SERVER['QUERY_STRING'])){
  248. $_sysVars['dolr_url'].='?'.$_SERVER['QUERY_STRING'];
  249. }
  250. return $_sysVars;
  251. }
  252. /**
  253. * 获取模板文件路径
  254. *
  255. * @param string $tplFileName 模板文件
  256. * @return string 文件名
  257. */
  258. protected function _getTplPath($tplFileName)
  259. {
  260. return $this->_options['template_dir'].$this->_trimPath($tplFileName);
  261. }
  262. /**
  263. * 获取缓存的文件
  264. * @param string $tplFileName 模板文件
  265. * @return string 文件名
  266. */
  267. protected function _getCacheFile($tplFileName, $cacheId)
  268. {
  269. return $this->_options['cache_dir'].$tplFileName.'.cache.'.md5($cacheId).'.php';
  270. }
  271. /**
  272. * 获取编译的文件名
  273. *
  274. * @param string $tplFileName 模板文件
  275. * @return string 文件名
  276. */
  277. protected function _getCompileFile( $tplFileName, $cacheId)
  278. {
  279. return $this->_options['compile_dir'].$tplFileName.'.compile.php';
  280. }
  281. /**
  282. * 解析模板
  283. *
  284. * @param string $tplFileName 模板文件
  285. * @return string 解析后的内容
  286. */
  287. protected function _parseTpl($tplFileName,$cacheId)
  288. {
  289. $tplFilePath=$this->_getTplPath($tplFileName);
  290. if (!file_exists($tplFilePath) or !is_readable($tplFilePath)) {
  291. $this->_throwException('不能打开指定的模板文件"'.$tplFileName.'"');
  292. }
  293. $content=file_get_contents($tplFilePath);
  294. //检测包含,检测所有的include 'xxx.html'
  295. preg_match_all('/[\n\r\t]*<\s*include\s+[\'"]\s*(.*?)\s*[\'"]\s*>[\n\r\t]*/s', $content, $matches);
  296. if(!empty($matches[1])){
  297. foreach ($matches[1] as $key=>$fileName) {
  298. $includeFilePath=$this->_getTplPath($fileName);
  299. if (!file_exists($includeFilePath) or !is_readable($includeFilePath)) {
  300. $this->_throwException('不能打开指定的模板文件"'.$includeFilePath.'"');
  301. }
  302. $includeCompilePath=$this->_getCompileFile($fileName,$cacheId);//得到编译文件名
  303. $this->_parseTpl($fileName,$cacheId);
  304. $content=str_replace($matches[0][$key], '<?php include \''.$includeCompilePath.'\';?>', $content);
  305. }
  306. }
  307. //规则
  308. $pattern=array(
  309. '/ \?\>[\n\r]*\<\? /s',//删除 PHP 代码断间多余的空格及换行
  310. '/(<\s*(\$[^>]+)\s*>)/es',//直接输出变量内容
  311. '/<\s*include\s+[\'"]\s*(.*?)\s*[\'"]\s*>/s',//包含模板
  312. '/<\s*if\s+(.+?)>(.+?)<\/if>/ies',//if /if
  313. '/<\s*elseif\s+(.+?)>/ies',//elseif
  314. '/<\s*else\s*>/is',//else
  315. '/<\s*loop\s+(\S+)\s+(\S+)>(.+?)<loopelse>(.+?)<\/loop>/ies',//loop $array $v
  316. '/<\s*loop\s+(\S+)\s+(\S+)>(.+?)<\/loop>/ies',//loop $array $v
  317. '/<\s*loop\s+(\S+)\s+(\S+)\s+(\S+)>(.+?)<loopelse>(.+?)<\/loop>/ies',//loop $array $k $v loopelse
  318. '/<\s*loop\s+(\S+)\s+(\S+)\s+(\S+)>(.+?)<\/loop>/ies',//loop $array $k $v
  319. '/<\s*cycle\s+\'(\S+)\'\s*,\s*\'(\S+)\'>/',//<cycle 'a','b'>
  320. );
  321. //PHP格式
  322. $php=array(
  323. ' ',
  324. "\$this->_parseVar('\\1')",
  325. '<?php include \''.$this->_options['template_dir']."\\1';?>",
  326. "\$this->_stripvtags('<?php if(\\1) { ?>','\\2<?php } ?>');",
  327. "\$this->_stripvtags('<?php } elseif(\\1) { ?>','');",
  328. '<?php } else { ?>',
  329. "\$this->_stripvtags('<?php if(!empty(\\1) and is_array(\\1)) {\$dolr_index=0; \nforeach(\\1 as \\2) { ?>','\\3<?php \$dolr_index++;} }else{?>\\4<?php } ?>');",
  330. "\$this->_stripvtags('<?php if(!empty(\\1) and is_array(\\1)) {\$dolr_index=0; foreach(\\1 as \\2) { ?>','\\3<?php \$dolr_index++;} } ?>\n');",
  331. "\$this->_stripvtags('<?php if(!empty(\\1) and is_array(\\1)) {\$dolr_index=0; foreach(\\1 as \\2 => \\3) { ?>','\\4<?php \$DolrView_index++;} }else{?>\\5<?php } ?>');",
  332. "\$this->_stripvtags('<?php if(!empty(\\1) and is_array(\\1)) {\$dolr_index=0; foreach(\\1 as \\2 => \\3) { ?>','\\4<?php \$DolrView_index++;} } ?>');",
  333. "<?php if(\$dolr_index%2){echo '\\2';}else{echo '\\1';}?>",
  334. );
  335. $content=preg_replace($pattern, $php, $content);
  336. //包含插件
  337. $pluginsString='';
  338. if($tplFileName==$this->mainTplFileName){//如果为主模板则放入include文件
  339. foreach ($this->includePlugins as $plugin) {
  340. $pluginsString.="include \$this->_options['plugins_dir'].'{$plugin}';\n";
  341. }
  342. }
  343. $header=<<<DOLRVIEWS
  344. <?php
  345. /**
  346. * {$tplFileName}
  347. * DolrViews 模板编译文件
  348. * @package {$this->mainTplFileName}
  349. */
  350. defined('DOLRVIEWS') or exit('Access denied');
  351. $pluginsString
  352. ?>\n
  353. DOLRVIEWS;
  354. $content=$header.$content;
  355. $compileFilePath=$this->_writeCompile($tplFileName,$content,$cacheId);//写入编译文件
  356. return $compileFilePath;
  357. }
  358. /**
  359. * 缓存模板文件
  360. *
  361. * @param string $tplFileName 模板文件
  362. * @param string $cacheId 缓存ID
  363. */
  364. protected function _writeCache($tplFileName,$cacheId)
  365. {
  366. $tplFilePath=$this->_getTplPath($tplFileName);
  367. $cacheFilePath=$this->_getCacheFile($tplFileName,$cacheId);//保存文件名
  368. $compileFilePath=$this->_getCompileFile($tplFileName,$cacheId);
  369. ini_set('error_reporting','off');//不缓存错误提示
  370. extract($this->_tpl_vars);//模板变量
  371. extract($this->_getSystemVars($tplFileName));//系统变量
  372. ob_start();
  373. if(file_exists($compileFilePath) and filemtime($compileFilePath)>filemtime($tplFilePath))//模板没有改动
  374. {
  375. include $compileFilePath;
  376. }else{
  377. include $this->_parseTpl($tplFileName,$cacheId);//解析并写入编译文件
  378. }
  379. $html=ob_get_contents();
  380. ob_clean();
  381. file_put_contents($cacheFilePath, $html);
  382. return $cacheFilePath;
  383. }
  384. /**
  385. * 写入编译文件
  386. *
  387. * @param string $tplFileName 模板文件
  388. * @param string $cacheId 缓存ID
  389. * @param string $content 网页
  390. */
  391. protected function _writeCompile($tplFileName,$content,$cacheId)
  392. {
  393. $compileFilePath=$this->_getCompileFile($tplFileName,$cacheId);//保存文件名
  394. if(!file_exists(dirname($compileFilePath))){
  395. $this->_makeDir(dirname($compileFilePath));
  396. }
  397. file_put_contents($compileFilePath,$content);
  398. return $compileFilePath;
  399. }
  400. /**
  401. * 将路径修正为适合操作系统的形式
  402. *
  403. * @param string $path 路径名称
  404. * @return string
  405. */
  406. protected function _trimPath( $path)
  407. {
  408. return rtrim(str_replace(array('/', '\\', '//', '\\\\'),DIR_SEP, $path),DIR_SEP);
  409. }
  410. /**
  411. * 根据指定的路径创建不存在的文件夹
  412. *
  413. * @param string $path 路径/文件夹名称
  414. * @return string
  415. */
  416. protected function _makeDir( $path)
  417. {
  418. $dirs = explode(DIR_SEP, $this ->_trimPath($path));
  419. $tmp = '';
  420. foreach ($dirs as $dir)
  421. {
  422. $tmp .= $dir . DIR_SEP;
  423. if (!file_exists($tmp) && [email protected]($tmp, 0777))
  424. return $tmp;
  425. }
  426. return true;
  427. }
  428. /**
  429. * 变量处理
  430. *
  431. * @param string $string 目标字符串
  432. * @return string
  433. */
  434. protected function _parseVar($string){
  435. $pattern=array(
  436. '/^</',
  437. '/>$/',
  438. '/(\$\w+\|[^>\s]+)/e',//$title|striptags|html2text
  439. '/(\$[\w]+\.[\w]+)/e',
  440. );
  441. $replacement=array(
  442. "<?php echo ",
  443. ' ?>',
  444. "\$this->_parseModifier('\\1');",
  445. "\$this->_parsePoint('\\1');",
  446. );
  447. return stripslashes(preg_replace($pattern, $replacement, $string));
  448. }
  449. /**
  450. * 变量调节器的处理
  451. *
  452. * @param string $string 模板中匹配到的变量
  453. * @return string 处理后的字符串
  454. */
  455. protected function _parseModifier($string)
  456. {
  457. $arr=explode('|', trim($string,';'));
  458. $tmp=array_shift($arr);
  459. foreach($arr as $value)
  460. {
  461. $tmpArr=explode(':',$value);//html2text
  462. $funcName=array_shift($tmpArr);//html2text
  463. $args=count($tmpArr)>0?','.join(',',$tmpArr):'';//参数用,号链接 arg1,arg2,arg3
  464. if(!function_exists($funcName)){//如果不是PHP内置函数则包含插件
  465. if(!file_exists($this->_options['plugins_dir'].'modifier_'.$funcName.'.php')){
  466. $this->_throwException('插件"'.$funcName.'"不存在');
  467. }
  468. $pluginFileName='modifier_'.$funcName.'.php';
  469. if(!in_array($pluginFileName, $this->includePlugins)){
  470. $this->includePlugins[]=$pluginFileName;//添加include插件
  471. }
  472. $tmp="dolr_modifier_{$funcName}($tmp{$args})";
  473. }else{
  474. $tmp="{$funcName}($tmp{$args})";
  475. }
  476. }
  477. return stripslashes($tmp.';');
  478. }
  479. /**
  480. * 数组操作的点支持
  481. *
  482. * @param string $string 目标字符串
  483. * @return string
  484. */
  485. protected function _parsePoint($string)
  486. {
  487. $arr=explode('.',$string);//$a.b.c.f
  488. $varName=array_shift($arr);//$a
  489. return $varName.'[\''.join('\'][\'',$arr).'\']';//$a['b']['c']['f']
  490. }
  491. /**
  492. * 去掉自定义标签
  493. *
  494. * @param string $expr 源文
  495. * @param string $statement 替换目标
  496. * @return string
  497. */
  498. protected function _stripvtags($expr, $statement)
  499. {
  500. $expr = str_replace("\\\"", "\"", preg_replace("/\<\?php echo \\\$.+? \?\>/s", "\\1", $expr));
  501. $statement = str_replace("\\\"", "\"", $statement);
  502. return $expr . $statement;
  503. }
  504. /**
  505. * 抛出一个错误信息
  506. *
  507. * @param string $message
  508. * @return void
  509. */
  510. protected function _throwException($message)
  511. {
  512. trigger_error('DolrViews错误:'.$message);
  513. exit;
  514. }
  515. }
  516. /* vim: set expandtab: */

使用范例:
PHP:
  1. <?php
  2. include 'DolrViews.class.php';
  3. $tpl=DolrViews::getInstance();//单例
  4. $_options=array(
  5. 'template_dir'=>'templates',//模板目录
  6. 'compile_dir'=>'templates_c',//编译目录
  7. 'cache_dir'=>'cache',//缓存目录
  8. 'caching'=>false,//是否缓存
  9. 'cache_lifetime'=>3600,//缓存有效期
  10. 'plugins_dir'=>'plugins',//插件目录
  11. 'tpl_suffix'=>'html',//模板后缀
  12. );
  13. $tpl->setOptions($_options);//保存如上设置
  14. $title="标题<span>测试啊</span>";
  15. $val=3;
  16. $array=array(
  17. array('username'=>'Joychao','password'=>'nowpass'),
  18. array('username'=>'DolrPHP','password'=>'password'),
  19. );
  20. $var='这里是带<span>标签</span>的HTML';
  21. //分配变量
  22. $tpl->assign('val',$val);
  23. $tpl->assign('title',$title);
  24. $tpl->assign('array',$array);
  25. //显示
  26. $tpl->display('index.html');
模板index.html:
  1. <html>
  2. <head>
  3. <title><$title|strip_tags:'hahah'|html2text:"nnn"></title>
  4. </head>
  5. <body>
  6. --包含文件----------------------------<br />
  7. <include 'header.html' >
  8. ------if else------------------------<br />
  9. <if $val==2>
  10. do anything
  11. <elseif $val==3>
  12. do another
  13. <else>
  14. Do not
  15. </if>
  16. ---------------------------------<br />
  17. ------loop------------------------<br />
  18. <loop $array $v>
  19. <p>循环内容<$dolr_index></p>
  20. 交互式显示:<cycle 'a','b'>
  21. <div>循环中的变量:<$v.username></div>
  22. <p>循环中的索引$dolr_index:<if $dolr_index%2==0>ccc</if></p>
  23. <loopelse>
  24. <div>没有内容</div>
  25. </loop>
  26. ---------------------------------<br />
  27. --包含文件----------------------------<br />
  28. <include 'footer.html' >
  29. ------------------------------<br />
  30. </body>
  31. </html>

目录范例:
DolrViews/
|--cache //缓存目录
|--plugins //插件目录
|--templates //模板目录
|--templates_c //编译文件目录
|--DolrViews.classs.php //引擎主文件
|--index.php //测试文件


目前插件只写了一个测试,插件规范是modifier_函数名.php 内容函数名:dolr_modifier_函数名($第一个参数必须[,其它参数])。范例:
  1. function dolr_modifier_html2text($string)
  2. {
  3. return strip_tags($string);
  4. }

今天刚写的,欢迎大家拍砖啊! 另外欢迎@安正超 !

  相关解决方案