A-A+

自己的samrty模板引擎仿写复杂版本(一)

2015年01月20日 php 暂无评论 阅读 83 次

前面分享过smarty模板引擎的思路和简单版本,今天分享自己的samrty模板引擎仿写复杂版本.

在跟目录建index.php文件,内容:

<?php
header ( 'Content-Type:text/html;charset=utf-8' );
//网站跟目录
define ( 'ROOT_PATH' , $_SERVER ['DOCUMENT_ROOT'].'/zero/' );
//存放模板文件夹
define('ZR_DIR' , ROOT_PATH.'templates/');
//编译文件夹
define('ZR_C_DIR' , ROOT_PATH.'templates_c/');

require ROOT_PATH.'class/zero.class.php';

$ZR = new Zero();

$ZR->assign('name','潘泽学的模板');
$ZR->assign('if',true);
$array = array('1'=>'1','2'=>'2');
$ZR->assign('array',$array);
$ZR->debug('index.html');
$ZR->display('index.html');

?>

在跟目录建templates文件夹,再在templates文件夹内建index.html文件,内容:

<html>
<head>
<title></title>
<meta content="">
<style></style>
</head>
<body>
{$name}
{#}........{#}
{if $if}
<div>当$if等于true时会显示这句话</div>
{/if}
foreach语句<br>
{$array->foreach(key,value)}
{@key}=>{@value}<br>
{/foreach}
</body>
</html>

在跟目录建templates_c文件夹,无内容但此文件夹要有写权限。

在跟目录建class文件夹。

在class文件夹内建zero.class.php文件,内容:

<?php

class Zero{

//保存模板变量
private $_zr_vars;
//保存模板文件名
private $_zr_file;
//保存ZoreParser对象(编译对象)
private $_parser;
//保存ZoreDebug对象(调试对象)
private $_debugger;
//构造函数
public function __construct(){
//判断模板文件夹和编译文件夹是否存在
if(!is_dir(ZR_DIR) || !is_dir(ZR_C_DIR)){
exit('错误:请正确设置模板文件夹和编译文件夹');
}
}
//设置模板变量
public function assign($var,$value){
//判断$var是否有值
if(isset($var) && trim($var) != ''){
$this->_zr_vars[$var] = $value;
return true;
} else {
exit('错误:请设置变量名');
}
}
//检测模板是否更新,更新则再编译,没有更新就用原来编译后的文件
public function display($zr_file){
$template_file = ZR_DIR.$zr_file;
//判断模板文件是否存在
if(!file_exists($template_file)){
exit('错误:模板文件不存在');
}

$parsed_file = ZR_C_DIR.md5($zr_file).'.php';
//if(!file_exists($parsed_file) || filemtime($parsed_file) < filemtime($template_file)){
if(TRUE){
require_once ROOT_PATH.'class/zero_parser.class.php';

$this->_parser = new ZeroParser();

$this->_parser->compile($zr_file);
}

include $parsed_file;

}
public function debug($zr_file){
if(include_once(ROOT_PATH.'class/zero_debugger.class.php')){
$this->_debugger = new ZeroDebuger($zr_file);
$this->_debugger->start();
} else {
exit('错误:Debuger类文件不存在');
}
}
}
?>

在class文件夹内建zero_debugger.class.php文件,内容:

<?php

class ZeroDebuger{

private $template; //用来保存模板的内容

private $IF_LINE_NUM; //用来保存IF所在的行数

private $ENDIF_LINE_NUM; //用来保存ENDIF所在的行数

private $FOREACH_LINE_NUM; //用来保存FOREACH所在的行数

private $ENDFOREACH_LINE_NUM; //用来保存ENDFOREACH所在的行数

private $ERRORS; //用来保存错误信息

/* 构造函数 */
public function __construct($file_name = NULL){
if(isset($file_name) && trim($file_name) != ''){
$this->set_file($file_name);
}
}

/* 打开模板文件并读取文件内容 */
public function set_file($file_name){

$file_name = ZR_DIR.$file_name;

if(file_exists($file_name)){

$fp = fopen($file_name,'r') OR exit('错误:不能打开模板文件');

if(($fs = filesize($file_name)) > 0)
$this->template = fread($fp,$fs);

fclose($fp);

return true;
}

exit('错误:模板文件不存在');

}

function start($display = FALSE){
/* 判断是否有{#} (注释) */
if(strpos($this->template,'{#}') != false){
/* 判断是否有{#}......{#} */
if(!preg_match("/\{#\}[^({#})]*\{#\}/" , $this->template)){
$this->ERRORS[] = '错误:{#}没有关闭';
}
}
/* 把模板内容按换行来分割成数组 */
$this->template = explode("\n" , $this->template);

/* 循行数组 */
$line_number =1;
foreach($this->template as $line){

/* 匹配{if....语句,并保存行数到IF_LINE_NUM变量中 */
if(preg_match("/\{\s*if/" , $line)){
$this->IF_LINE_NUM[] = $line_number;
}
/* 匹配{/if}语句,并保存行数到ENDIF_LINE_NUM */
if(preg_match("/\{\s*\/if\s*\}/" , $line)){
$this->ENDIF_LINE_NUM[] = $line_number;
}
/* 匹配{$array_name->foreach(key, value)....语句,并保存行数到FOREACH_LINF_NUM变量中 */
if(preg_match("/\{\s*\\$[0-9a-zA-Z_]+\->foreach\(\s*[0-9a-zA-Z_]+\s*,\s*[0-9a-zA-Z_]+\s*\)\s*/" , $line)){
$this->FOREACH_LINE_NUM[] = $line_number;
}
/* 匹配{/foreach}语句,并保存行数到ENDFOREACH变量中 */
if(preg_Match("/\{\s*\/foreach\s*\}/" , $line)){
$this->ENDFOREACH_LINE_NUM[] = $line_number;
}
/* 检测include语法是否正确(是否符合{include "......."}) */
if(strpos($line , "{include") != false){
if(!preg_match("/\{include \"[^}]*\"\}/" , $line)){
$this->ERRORS[] = '错误:INCLUDE语法使用错误,在行<font color="red">'.$line_number.'</font>';
}
}
$line_number++;
}
/* 调用if和foreach两个方法 */
$this->check_if();
$this->check_foreach();

if(count($this->ERRORS) > 0){
echo '<div style="border:1px solid #f60;padding:10px;width:500px;font-size:12px;color:#800;">';
foreach($this->ERRORS as $e){
echo $e,'<br/>';
}
echo '</div>';
} else {
if($display)
echo $this->highlight_html(implode('',$this->template));
echo '<div style="border:1px solid #f60;padding:10px;width:200px;font-size:12px;test- align:center;color:#800;">恭喜:模板没有错误</div>';
}
}
/* 检测{if .......}.....{/if}语句是否合理或是否关闭 */
private function check_if(){
$ifs = count($this->IF_LINE_NUM);

for($i = 0; $i <$ifs; $i++){
if(isset($this->ENDIF_LINE_NUM[$i])){
if($this->IF_LINE_NUM[$i] > $this->ENDIF_LINE_NUM[$i]){
$this->ERRORS[] = '错误:在没有IF的情况下使用END IF,在行<font color="red">'.$this->IF_LINE_NUM[$i].'</font>';
}
} else {
$this->ERRORS[] = '错误:IF没有关闭,在行<font color="red">'.$this->IF_LINE_NUM[$i].'</font>';
}
if(count($this->IF_LINE_NUM) == 0 && count($this->ENDIF_LINE_NUM) > 0){
foreach($this->ENDIF_LINE_NUM as $line_number){
$this->ERRORRS[] = '错误:在没有IF的情况下使用END IF,在行<font color="red">'.$line_number.'</font>';
}
}
}
print_r($this->FOREACH_LINE_NUM);
}
/* 检测{$array_name->foreach(key, value)}....................{/foreach}语句是否合理或是否关闭 */
private function check_foreach(){
$foreachs = count($this->FOREACH_LINE_NUM);
for($i = 0 ; $i <$foreachs ; $i++){
if(isset($this->ENDFOREACH_LINE_NUM[$i])){
if($this->FOREACH_LINE_NUM[$i] > $this->ENDFOREACH_LINE_NUM[$i]){
$this->ERRORS[] = '错误:在没有FOREACH的情况下使用END FOREACH,在行<font color="red">'.$this->ENDFOREACH_LINE_NUM[$i].'</font>';
}
} else {
$this->ERRORS[] = '错误:FOREACH没有关闭,在行<font color="red">'.$this->FOREACH_LINE_NUM[$i].'</font>';
}
}
if(count($this->FOREACH_LINE_NUM) == 0 && count($this->ENDFOREACH_LINE_NUM) > 0){
foreach($this->ENDFOREACH_LINE_NUM as $line_number){
$this->ERRORS[] = '错误:在没有FOREACH的情况下使用END FOREACH,在行<font color="red">'.$line_number.'</font>';
}
}
}
private function highlight_html($code){
$code = str_replace('<','<',$code);
$code = str_replace('>','>',$code);
$code = preg_replace('/([a-zA-Z_]+)=/','<font color="#FF0000">$1=</font>',$code);
$code = preg_replace('/(<[\/a-zA-Z0-9$;]+)/','<font color="#0000FF">$1</font>',$code);
$code = str_replace('<!--','<font color="#008080"><!--',$code);
$code = str_replace('-->','--></font>',$code);
$code = n12br($code);
return $code;
}
}
?>

在class文件夹内建zero_parser.class.php文件,内容:

<?php

class ZeroParser{
/* 用来保存模板内容 */
private $template;
/* 设置和读取模板 */
private function set_file($file){
/* 模板路径 */
$file = ZR_DIR.$file;

if(!file_exists($file)){
exit('错误:模板文件不存在');
}

/* 打开模板文件 */
$fp = fopen($file,'r');

if(!$fp) exit('错误:不能打开文件');

if(filesize($file)){
/* 读取模板文件的内容 */
$this->template = fread($fp,filesize($file));
} else {
exit('错误:模板文件大小为零');
}
/* 关闭打开的文件 */
fclose($fp);

return true;
}

/* 解析变量 */
private function _parse_var(){
/* 变量匹配正则 */
$patten = '/\{\\$([a-zA-Z0-9]+)\}/';

if(strpos($this->template,'{$') !== false){
/* 正则替换 */
$this->template = preg_replace($patten,"<?php echo \$this->_zr_vars['$1']; ?>",$this->template);
}
return true;
}
/* 解析IF条件语句 */
private function _parse_if(){
/* 判断模板文件中是否有IF语句 */
if(preg_match("/\{\s*if/",$this->template)){
if(preg_match('/\{\s*\/if\s*\}/',$this->template)){
$if_patten = "/\{\s*if\s+\\$([a-zA-Z0-9]+)\}/";
$ef_patten = "/\{\s*\/if\s*\}/";
$this->template = preg_replace($if_patten,"<?php if(\$this->_zr_vars['$1']){?>",$this->template);
$this->template = preg_replace($ef_patten,"<?php }; ?>",$this->template);
} else {
exit('错误:语法错误,没有封闭IF条件语句');
}
}
return true;
}
/* 解析注释语句 */
private function _parse_common(){
$patten = "/\{#\}([^{]*)\{#\}/";
if(strpos($this->template,'{#}') !== false){
$this->template = preg_replace($patten,"<?php /* $1 */ ?>",$this->template);
}
return true;
}
/* include语句 */
private function _parse_include(){
if(preg_match("/\{\s*include \"([^}]*)\"\s\*}/",$this->template,$file)){

if(trim($file[1]) == '') exit('错误:文件指定包含的文件');

if(!file_exists(trim($file[1]))) exit('错误:文件不存在');

$include_patten = '/{\s*include "([^]+)"\S*}/';

$include_patten = preg_replace($include_patten,"<?php include '$1'; ?>",$this->template);
}
return true;
}

/* 解析Foreach语句 */
private function _parse_foreach(){echo 'aa';
if(preg_match("/\{\s*\\$[0-9a-zA-Z_]+\->foreach\(\s*[0-9a-zA-Z_]+\s*,\s*[0-9a-zA-Z_]+\s*\)\s*/" , $this->template)){
echo 'dd';
if(preg_match("/{\s*\/foreach\s*}/",$this->template)){
/* 循环内的变量 如:{@key} */
if(preg_match("/\{\s*@[0-9a-zA-Z_]*/",$this->template)){

$k_and_v_patten = "/\{\s*@([0-9a-zA-Z_]+)\s*\}/";

$this->template = preg_replace($k_and_v_patten,"<?php echo \$$1; ?>", $this->template);
}
$foreach_patten = "/\{\s*\\$([0-9a-zA-Z_]+)\s*\->\s*foreach\(\s*([0-9a-zA-Z_]+)\s*,\s*([0-9a-zA-Z_]+)\s*\)\s*\}/";

$end_foreach_patten = "/\{\s*\/foreach\s*\}/";

$this->template = preg_replace($foreach_patten,"<?php foreach(\$this->_zr_vars['$1'] as \$$2 => \$$3){ ?>",$this->template);

$this->template = preg_replace($end_foreach_patten,"<?php }; ?>",$this->template);
} else {

exit('错误:语法错误,没有封闭FOREACH条件语句');
}
}
}

/* compile方法 */
public function compile($file_name){

$this->set_file($file_name); //读取模板文件

$this->_parse_var(); //

$this->_parse_if(); //解析if语句

$this->_parse_common(); //解析注释语句

$this->_parse_foreach(); //解析foreach()语句

$this->_parse_include(); //解析include()语句

$zr_c_file = ZR_C_DIR.md5(basename($file_name)).'.php'; //产生缟译后的文件名

$fp = fopen($zr_c_file,'w');

fwrite($fp,$this->template);

fclose($fp);
}
}

?>

在是和smarty一样要编译后才可运行的模板引擎,下面在说个不用编译的,这总方法就有点像cake自带的模板引擎。不说这什么多了,先看看代码吧。

建个index.php文件,内容:

<?php
/**
* 检查zero类是否已定义
*/
if(!class_exists('zero')){
require 'zero.php';
}

class controller{

private $zero = NULL;

public function __construct(){
$this->zero = new zero();
/**
* 告诉模板引擎加载哪个模板
*/
$this->zero->load('index.html');
}

public function index(){
$parameter = '我是由模板引擎传过来的!';
$this->zero->set('parameter',$parameter);
$this->zero->set('arr',array(1,2,3,4,5,6,7));
}
}

$cont = new controller();
$cont->index();
?>

建个zero.php文件,内容:

<?php
/**
* 模板引擎
*/
class zero{
//模板
private $templet = NULL;
//变量数组
private $variable = array();

public function __construct(){

}

public function load($path){
$this->templet = $path;

}

public function set($key,$value){
$this->variable = array_merge($this->variable,array(
$key=>$value
));
}

function __destruct(){
extract($this->variable,EXTR_PREFIX_SAME,'zero');
require dirname ( __FILE__ ).'/Temp/'.$this->templet;
}
}
?>

相关文章:

如何自己写smarty模板引擎-smarty仿写之思路(一)

smarty模板引擎简单思路的实现-smarty仿写之思路(二)

自己的samrty模板引擎仿写简单版本(一)

自己的samrty模板引擎仿写简单版本(二)

标签:

给我留言

Copyright © web前端技术开发个人博客 保留所有权利  京ICP备14060653号 Theme  Ality

用户登录