php模板引擎的原理与简单实例
模板引擎其实就是将一个带有自定义标签的字符串,通过相应的规则解析,返回php可以解析的字符串,这其中正则的运用是必不可少的,所以要有一定的正则基础。
总体思想,引入按规则写好的模板,传递给标签解析类(_HtmlTag)进行解析,再把解析好的字符串传递给php进行解析渲染输出
首先定义了一个_HtmlTag类:
class _HtmlTag{
protected $taglist = "if|elseif|else|loop|for|while|=|:=|:e|:html|:";
protected $string;
protected $tpldir;
function __construct($dir, $tpl) {
$this->findReg = "/\\{[ ]*($this->findReg)(\\s*[\\{\\}]+\\s*)?\\}/i";
$this->tpldir = $dir;
$this->tpl =$tpl;
}
}
先解析下render方法
在render方法中对传入的字符串$s,进行相应的替换渲染,用到了extends方法,import方法,''开头的各种过滤方法,后面会一一进行详解.function render($s) {
$s = str_replace(['<--{', '}-->'], ['{','}'], $s);
if (strpos($s, "{extends") !== false) {
$s = $this->_extends($s);
}
$s = preg_replace('/[\\n\\r]+/s', "\\n", $s);
$s = preg_replace('/\\r+/s', "", $s);
$s = preg_replace('/\\t+/s', "\\t", $s); $this->string = str_replace(["\\{", "\\}"], ["\\HKH1", "\\HKH2"], $s);
preg_match_all("/\\{[ ]*(import)(\\s*[^\\{\\}]+\\s*)?\\}/i", $s, $d);
foreach($d[0] as $k => $v) {
call_user_func_array(array($this, "_import"), array($v, $d[1][$k], $d[2][$k]));
}
static $m = array("=" => "_defaultEcho", ":=" =>" _xssEcho", ":e" => "_htmlencodeEcho", ":" => "_echo", ":html" => "_htmldecodeEcho");
preg_match_all($this->findReg, $this->string, $d);
foreach ($d[0] as $k => $v) {
if (isset($m[$d[1][$k]])) {
$medth = $m[$d[1][$k]];
} else {
$medth = '_' . $d[1][$k];
}
call_user_func_array(array($this, $medth), array($v, $d[1][$k], $d[2][$k]));
}
}- _extends方法
使用 个方法是为了判断是否有继承相应的模板文件,在子文件中用{extends 模板名},一般只要填写文件名,如index,系统会自动去构造完整的文件路径,一般在上一级目录,这个在realTpl()方法时会有详解function _extends($tpl) {
preg_match("/\\{\\s*extends\\s+([^\\{\\}]+?)\\s*\\}/i", $tpl, $d);
list($search, $arg) = $d;
if (stripos($arg, ".") !==0 ) {
$arg = '../' . $arg;
}
$file = $this->tpl->me->realTpl($arg . ".htm");
$basetpl = file_get_contents($file);
preg_match_all("/\\{block\\s+([^\\{\\}]+?)\\s*\\}(.+)\\{\\/block\\}/s", $tpl, $ds);
foreach($ds[1] as $n => $name) {
$basetpl = preg_replace("/\\{block\\s+name={$name}\\s*\\}/", $ds[2][$n], $basetpl);
}
$basetpl = preg_replace("/\\{block\\s+name=.+?\\}/", ' ', $basetpl);
return $basetpl;
} - _import 方法
_import 方法是用来对{import 文件名}标签进行解析,默认也是上层目录,会把该文件的内容解析到当前标签位置,同时支持使用:函数名|参数1, 参数2,...的方式进行字符串的回调.这中的display方法后面会有详解function _import ($search, $tag, $arg) {
$arg = trim($arg);
if (stripos($arg, ".") !== 0) {
$arg = '../' . $arg;
}
$file = $this->tpl->me->realTpl($arg . ".htm");
if (file_exists($file)) {
$this->string = str_replace($search, file_get_contents($file), $this->string);
return;
} else {
if (stripos($file, "$") !== false) {
$this->string = str_replace($search, '<? include $this->tpl->display("' . $arg . '"); ?>', $this->string);
return;
}
if (stripos($arg, '|') !== false) {
list($func, $tmp) = explode("|", trim($arg));
$kw = explode(', ', $tmp);
} else {
$func = trim($arg);
$kw = array();
}
if (function_exists($func) {
$tpl_str = call_user_func_array($func, $kw);
$this->string = str_replace($search, $tpl_str, $this->string);
return;
} else {
$this->string = str_replace($search, $arg, $this->string);
return;
}
system_error($this->tpldir . trim($arg) . ".htm不存在");
}
} 现在来看一个标签函数_loop,将标签{loop $data $k $v}或{loop $data $v}替换,实现模板循环,其中的parseAttr方法接下来将详解
function _loop($search, $tag, $arg) {
list($attr, $arg) = $this->parseAttr($arg);
$d = preg_split("/\\s+/", trim($arg));
if (count($d) == 3) {
$data = $d[0];
$k = $d[1];
$v = $d[2];
$s = "<? \\n if(!empty($data)) {\\n \\t foreach(" . $data . " as {$k} => {$v}){";
} elseif (count($d) == 2) {
$data = $d[0];
$v = $d[1];
$s = "<? \\n if(!empty($data)){\\n \\t
foreach(". $datsa . " as {$v}) {";
} if (isset($attr['counter'])) {
$s = "\\n \\t\\t<? if (!isset({$attr['counter']})) { {$attr['counter']} = 0;} ?> \\n \\t\\t" . $s . "\\n \\t\\t{$attr['counter']}++; \\n ?>;
} else {
$s .= "?>";
}
$this->string = str_replace($search, $s, $this->string);
$this->string = str_replace("{/loop}", "<? \\t}\\n}?>", $this->string);
}- parseAttr方法
该方法用来解析标签 的参数,返回的第一个参数是值形式字符串,第二个是键值数组,如{loop $data $k $v}返回$data $k $v 组成 的字符串和一个空数组,{if $k = $v}则返回空字符串和和数组[$k => $v];function parseAttr($s) {
$reg = '/([a-zA-Z0-9_])+\\s*=\\s*([a-zA-Z0-9_\\$]+)/i';
$arg = preg_replace($reg, '', $s);
preg_match_all($reg, $s, $d);
$arr = array();
foreach($d[1] as $key => $value) {
$arr[trim($value)] = trim($d[2][$key]);
}
return array($arr, $arg);
} - _if 方法
将{if}{/if}标签解析成原生 的php,简单明了,不多解释function _if ($search, $tag, $arg) {
$replace[0] = "<? if($arg){?>";
$replace[1] = "<? }?>";
$this->string = str_replace(array($search, "{/$tag}"), $replace, $this->string);
} - _else方法
将{else}解析成原生的phpfunction _else($search, $tag, $arg) {
$this->string = str_replace("{else}", "\\n" . '<? } else {?>', $this->string);
} - _elseif方法
将{elseif}解析成原生的phpfunction _elseif($search, $tag, $arg) {
$this->string = str_replace($search, "<? }elseif($arg){?>", $this->string);
} - _for 方法
function _for($search, $tag, $arg) {
$s = trim(trim(preg_replace('/\\s+/', ' ; ', $arg)), ';');
$this->string = str_replace($search, "<? for($s){?>", $this->string);
$this->string = str_replace("{/for}", "<? \\t\\n}?>", $this->string);
} - _while方法
function _while($search, $tag, $arg) {
$this->string = str_replace($search , "<? while($aeg){ ?>", $this->string);
$this->string = str_replace("{/while}", "<? \\t\\n} ?>", $this->string);
} _end方法
通过这个方法对其$this->string进行修改可以对变量进行如下标签定义:
{$val}{$arr[$key]}{$arr['key']}{:val()}{$arr.key}{$arr.$key}function _end() {
//{$val}标签解析
$this->string = preg_replace('/\\{(\\$\\w+.*)\\}/is', "<?=\\\\1;?>", $this->string);
//{:$val()}标签解析
$this->string = preg_replace('/\\{\\:\\s*(\\$?\\w+.*?)\\}/is', "<? =\\\\1;?>", $this->string);
//将$val[arg]解析成$val['arg']
$this->string = preg_replace('/\\$([_a-z]+\\w*)\\[([_a-z]+\\w*)\\]/is', "$\\\\1['\\\\2']", $this->string);
//支持点号访问数组。如array['key']可以用array.key访问
$this->string = preg_replace('/\\$([_a-z]+\\w*)\\.(\\$[_a-z]+\\w*)/is', "$\\\\1[\\\\2]", $this->string);
$this->string = preg_replace('/\\$([_a-z]+\\w*)\\.([_a-z]+\\w*)/is', "$\\\\1['\\\\2']", $this->string);
$this->string = preg_replace(array("/<\?/", "/<?\php\s*=/"), array("<?php ", "<?php echo "), $this->string);
}_result方法,做最后处理,返回字符串,_end方法在上面已经解释过了
function result() {
$this->_end();
$this->_baseParse($this->stirng);
return str_replace(array("\\HKH1", "\\HKH2"), array("\\{", "\\}"), $this->string);
}下面再来介绍一下几个过滤的方法
_htmlencodeEcho方法
可以用此种形式对变量进行htmlspecialchars式的过滤spspecialchars函数后面会有介绍{:e $val}function _htmlencodeEcho($search, $tag, $arg) {
if (stripos($arg, '$')) {
$this->string = str_replace($search, "<? echo sphtmlspecialchars(!empty($arg)?$arg:''))?>", $this->string);
} else {
$this->string = str_replace($search, "<? echo sphtmlspecialchars($arg)?>", $this->string);
}
}- _htmldecodeEcho方法
_htmldecodeEcho($search, $tag, $arg) {
if (stripos($arg, '$')) {
$this->string = str_replace($search, "<? echo htmlspecialchars_decode(!empty($arg)?$arg:'')?>", $this->string);
} else {
$this->string = str_replace($search, "<? echo htmlspecialchars_decode($arg)?>", $this->string);
}
} - _defaultEcho方法
function _defaultEcho($search, $tag, $arg) {
if (stripos($arg, ',') == false) {
$arg . = ", ' ' ";
list($v, $default) = explode(",", $arg);
$this->string = str_replace($search, "<? echo empty($v)?$default:$v; ?>", $this->string);
}
} _xssEcho方法
_xssEcho($search, $tag, $arg) {
$this->string = str_replace($search, "<? echo xssRemove($arg); ?>", $this->string);
}_echo方法
function _echo($search, $tag, $arg){
$this->string = str_replace($search, "<? echo $arg;?>", $this->string);
}_baseParse方法,这个对css和js的引入进行处理
function _baseParse($s) {
if (strpos($s, "{loadCss") !== false) {
$s = $this->_loadCss($s);
}
if (strpos($s, "{loadJs") !== false) {
$s = $this->_loadJs($s);
}
return $s;
}- _loadJs方法
function _loadJs($tpl) {
preg_match_all('/\{loadJs\s+([^\{\}]+?)\s*\}/i', $tpl, $match);
include "jsmin.php";
foreach($match[1] as $k => $js) {
$jsstr = "";
$cdn = "";
$if (stripos($js, ',') !== false) {
$filename = "";
$md5 = "";
$js_content = "";
foreach(explode(',', $js) as $j) {
$filename .= basename($j, '.js') . ',';
$md5 .= self::fileMd5($j);
if (stripos($j, '.min.') == false) {
$js_content .= JSMin::minify(file_get_contents(WEB_ROOT . $j));
} else {
$js_content .= file_get_contents(WEB_ROOT . $j);
}
$js_content .= ';'
}
$md5 = md5($md5);
$filename = dirname($j) . '/' .trim($filename, ',') . '.js';
self::parseJs($js_content, $filename);
$jsstr = "<script src=\"{$cdn}{$filename}?v={$md5}\"></script>";
} else {
$md5 = self::fileMd5($js);
if (stripos($js, ".min.") === false) {
$js = self::pareJs(JSMin::minify(file_get_contents(WEB_ROOT . $js)), dirname($js). '/' .basename($js, '.js') . '.min.js');
}
$jsstr = "<script src=\"{$cdn}{$js}?v={$mdt}\"></script>";
}
$tpl = str_replace($match[0][$k], $jsstr, $tpl);
}
return $tpl;
}_loadCss方法
function _loadCss($tpl) {
preg_match_all('/\{loadCss\s+([^\{\}]+)\s*\}/i' , $tpl , $match);
$cdn = "";
foreach($match[1] as $k => $css) {
$cssstr = "";
if (stripos($css, ',') !== false) {
$filename = "";
$md5 = "";
$css_content = "";
foreach($explode(',', $css) as $css) {
$filename .= basename($css, '.css') . ',';
$md5 = self::fileMd5($css);
$css_content .= file_get_contents(WEB_ROOT .$css);
}
$md5 = md5($md5);
$filename = dirname($css) . '/' .trim($filename, ',') . '.css';
self::parseCss($css_content, $filename);
$cssstr = "<link rel=\"stylesheet\" href=\"{$cdn}{$filename}?v={md5}\">";
} else {
$md5 = self::fileMd5($css);
if (stripos($css, ".min.") == false) {
$css = self::parseCss(file_get_contents(WEB_ROOT . $css), dirname($css) . '/' .basename($css, '.css') . '.min.css');
}
$cssstr .= "<link rel=\"stylesheet\" href=\"{$cdn}{$css}?v={$md5}\">";
}
$tpl = str_replace($match[0][$k], $cssstr, $tpl);
}
return $tpl;
} - 上面两个函数都用到了各自的parseCss,和parseJs,让我们来看下
static function parseCss($css_content, $filename) {
$css_content = preg_replace("/[\r\n\t]/", '', $css_content);
$css_content = preg_replace("/ +/", ' ', $css_content);
sp_file_pu_contents(WEB_ROOT . $filename, $css_content);
return $filename;
}
static function parseJs($js, $filename) {
sp_file_put_content(WEB_ROOT . $filename, $js);
return $filename;
}
上面的_html类和他的方法对传入的字符串进行了标签到原生 Php的替换,可以传入相应的模板,也可以在其中自定义自己的过滤方法。
下面我们再来看一个template类:
定义这个类来对模板进行管理,这里会用到上面的_html类,基本定义如下,接下来会介绍其中的一些方法
class template {
private $tpl_cache_dir;
public $ext = ".htm";
public $default_tpl_dir = false;
public $me; function _construct($tpl_cache_dir, $me) {
$this->sets($tpl_cache_dir);
$this->me = $me;
}
function sets($tpl_cache_dir) {
$this->tpl_cache_dir = $tpl_cache_dir;
if (SP_DEBUG == 1) {
spmkdir($this->tpl_cache_dir);
}
}
function default_template($dir) {
$this->default_tpl_dir = $dir;
}
}
getString方法
在之前 开启ob_start(),然后获取这之间的ob数据,试用于框架中function getString() {
$s = ob_get_contents();
ob_clean();
return $s;
}display方法
function display($tpl, $return = 0, $script = SCRIPT) {
$tpl .= $this->ext;
$tpl_name = $this->tpl_cache_dir . $script . '_' . str_replace(['/', '\\\\'], '_', $tpl);
if (SP_DEBUG){
if (SP_DEBUG == 1 || (SP_DEBUG == 2 && !file_exists($tpl_name))) {
$this->compiles($tpl_name, $tpl);
}
}
return $tpl_name;
}- compiles方法, 在这里使用了sp_file_put_contentsb函数会在后面介绍
function compiles($cache_name, $tpl) {
if (!file_exists($tpl)) {
if (file_exists($this->default_tpl_dir . $tpl)) {
$html = file_get_contents($this->default_tpl_dir . $tpl);
} else {
spmkdir(dirname($tpl), '777');
file_get_contents($tpl, 这个是自动生成模板');
}
} else {
$html = file_get_contents($tpl);
}
$tag = new _HtmlTag(realpath(dirname($tpl) . '/../'), $this);
$tag->render($html);
$html = preg_replace_callback('//s', array(&$this, "make_magic_func"), $tag->result());
$html = preg_replace('/[\\n\\r]{1,}/s', "\\n", $html);
@unlink($cache_name);
sp_file_put_contents($cache_name, $thml);
} - make_magic_func方法
function make_magic_func($d) {
preg_match_all('/(\\w+)=\\s*(\\'|\\")([^\\'"]+)?\\2/m', $d[2], $dd);
$aa = array();
foreach($dd[1] as $i => $k) {
$vs = preg_replace('/([^\\-]*[<>]{1,1})\\s*(\\S+)?\\s*/', '\\1\\'\\2\\' ', $dd[3][$i]);
$vs = preg_replace('/=\\s*(\\S+)?\\s*/', '=\\'\\1\\'', $dd[3][$i]);
if (stripos($d[3][$i], '$') !== false) {
$vs = preg_replace('/\\$([^\\']+)\\s*/', '{$\\1}', $vs);
}
}
}注意
- 两个类的相应方法都 定义好了,这其中用到的函数sp_file_pu_contents($file, $data)
function sp_file_put_contents($file, $data) {
$dir = dirname($file);
if (is_writeable($dir)) {
return file_put_contents($file, $data);
} else {
throw new writeFail(sprintf("写入文件%s,失败,目录不可写", $file), 503);
}
}js压缩类: jsmin.class.php
链接:http://pan.baidu.com/s/1kUBx0X5 密码:y5jb
将这两个 类定义在一个文件中,使用进进行相应的配置define("WEB_ROOT", dirname(__FILE__) . '/');
使用
文件准备
base.htm,main.htm,test.js,test1.js,test.css,test1.css,indx.php,im.htm
main.htm:
{import ./im}
{block name=head}
{block name=body}
{block name=foot}- base.htm
{extends ./main}
{block head}
{loadCss test.css,test1.css}
{loop $data $k $v}
<p>
我是头部
</p>
{/loop}
{if $bb == 'aa'}
<p>
我是if 测试
</p>
{elseif $bb = 'bb'}
<p>
我是elesif测试
</p>
{else}
<p>
我是eles测试
</p>
{/if}
{for $i=0 $i<10 $i++}
<p>
我是for循环
</p>
{/for}
{while $con >5}
<? $con--;?>
<p>
我是while循环
</p>
{/while}
{/block}
{block foot}
<p>
我是尾部
</p>
{loadJs test1.js,test.js}
{/block} - im.htm
我是import进来的
- index.php
<?php
define('WEB_ROOT',dirname(__FILE__) . '/');
function sp_file_put_contents($file, $data) {
$dir = dirname($file);
if (is_writeable($dir)) {
return file_put_contents($file, $data);
} else {
throw new writeFail(sprintf("写入文件%s,失败,目录不可写", $file), 503);
}
}
class _HtmlTag{
protected $taglist = "if|elseif|else|loop|for|while|=|:=|:e|:html|:";
protected $string;
protected $tpldir;
public $findReg;
function __construct($dir, $tpl) {
$this->findReg = "/\{[ ]*($this->taglist)(\s*[^\{\}]+\s*)?\}/i";
$this->tpldir = $dir;
$this->tpl =$tpl;
}
function render($s) {
$s = str_replace(['<--{', '}-->'], ['{','}'], $s);
if (strpos($s, "{extends") !== false) {
$s = $this->_extends($s);
}
$s = preg_replace('/[\n\r]+/s', "\n", $s);
$s = preg_replace('/\r+/s', "", $s);
$s = preg_replace('/\t+/s', "\t", $s);
$this->string = str_replace(["\{", "\}"], ["\HKH1", "\HKH2"], $s);
preg_match_all("/\{[ ]*(import)(\s*[^\{\}]+\s*)?\}/i", $s, $d);
foreach($d[0] as $k => $v) {
call_user_func_array(array($this, "_import"), array($v, $d[1][$k], $d[2][$k]));
}
static $m = array("=" => "_defaultEcho", ":=" =>" _xssEcho", ":e" => "_htmlencodeEcho", ":" => "_echo", ":html" => "_htmldecodeEcho");
preg_match_all($this->findReg, $this->string, $d);
foreach ($d[0] as $k => $v) {
if (isset($m[$d[1][$k]])) {
$medth = $m[$d[1][$k]];
} else {
$medth = '_' . $d[1][$k];
}
call_user_func_array(array($this, $medth), array($v, $d[1][$k], $d[2][$k]));
}
}
function _extends($tpl) {
preg_match("/\{\s*extends\s+([^\{\}]+?)\s*\}/i", $tpl, $d);
list($search, $arg) = $d;
if (stripos($arg, ".") !==0 ) {
$arg = '../' . $arg;
}
//$file = $this->tpl->me->realTpl($arg . ".htm");
$file = $arg . ".htm";
$basetpl = file_get_contents($file);
preg_match_all("/\{block\s+([^\{\}]+?)\s*\}(.+?)\{\/block\}/s", $tpl, $ds);
foreach($ds[1] as $n => $name) {
$basetpl = preg_replace("/\{block\s+name={$name}\s*\}/", $ds[2][$n], $basetpl);
}
$basetpl = preg_replace("/\{block\s+name=.+?\}/", ' ', $basetpl); return $basetpl;
}
function _import ($search, $tag, $arg) { $arg = trim($arg);
if (stripos($arg, ".") !== 0) {
$arg = '../' . $arg;
}
//$file = $this->tpl->me->realTpl($arg . ".htm");
$file = $arg . ".htm";
if (file_exists($file)) {
$this->string = str_replace($search, file_get_contents($file), $this->string);
return;
} else {
if (stripos($file, "$") !== false) {
$this->string = str_replace($search, '<? include $this->tpl->display("' . $arg . '"); ?>', $this->string);
return;
}
if (stripos($arg, '|') !== false) {
list($func, $tmp) = explode("|", trim($arg));
$kw = explode(', ', $tmp);
} else {
$func = trim($arg);
$kw = array();
}
if (function_exists($func)) {
$tpl_str = call_user_func_array($func, $kw);
$this->string = str_replace($search, $tpl_str, $this->string);
return;
} else {
$this->string = str_replace($search, $arg, $this->string);
return;
}
system_error($this->tpldir . trim($arg) . ".htm不存在");
}
} function mytest(){
return $this->string;
} function _baseParse($s) {
if (strpos($s, "{loadCss") !== false) {
$s = $this->_loadCss($s);
}
if (strpos($s, "{loadJs") !== false) {
$s = $this->_loadJs($s);
}
return $s;
} function fileMd5($f) {
if (stripos($f, "http://") !== false) {
return md5($f);
}
return md5_file(WEB_ROOT . $f);
} function _loadCss($tpl) {
preg_match_all('/\{loadCss\s+([^\{\}]+)\s*\}/i' , $tpl , $match);
$cdn = "";
foreach($match[1] as $k => $css) {
$cssstr = "";
if (stripos($css, ',') !== false) {
$filename = "";
$md5 = "";
$css_content = "";
foreach(explode(',', $css) as $css) {
$filename .= basename($css, '.css') . ',';
$md5 = self::fileMd5($css);
$css_content .= file_get_contents(WEB_ROOT .$css);
}
$md5 = md5($md5);
$filename = dirname($css) . '/' . trim($filename, ',') . '.css';
self::parseCss($css_content, $filename);
$cssstr = "<link rel=\"stylesheet\" href=\"{$cdn}{$filename}?v={$md5}\">";
} else {
$md5 = self::fileMd5($css);
if (stripos($css, ".min.") == false) {
$css = self::parseCss(file_get_contents(WEB_ROOT . $css), dirname($css) . '/' .basename($css, '.css') . '.min.css');
}
$cssstr .= "<link rel=\"stylesheet\" href=\"{$cdn}{$css}?v={$md5}\">";
}
$tpl = str_replace($match[0][$k], $cssstr, $tpl);
}
return $tpl;
} function _loadJs($tpl) { preg_match_all('/\{loadJs\s+([^\{\}]+?)\s*\}/i', $tpl, $match);
//var_dump($match);die;
include "jsmin.class.php";
foreach($match[1] as $k => $js) {
$jsstr = "";
$cdn = "";
if (stripos($js, ',') !== false) {
$filename = "";
$md5 = "";
$js_content = "";
foreach(explode(',', $js) as $j) {
$filename .= basename($j, '.js') . ',';
$md5 .= self::fileMd5($j);
if (stripos($j, '.min.') === false) {
$js_content .= JSMin::minify(file_get_contents(WEB_ROOT . $j));
} else {
$js_content .= file_get_contents(WEB_ROOT . $j);
}
$js_content .= ';';
}
$md5 = md5($md5); $filename = dirname($js) . '/' .trim($filename, ',') . '.js';
self::parseJs($js_content, $filename);
$jsstr = "<script src=\"{$cdn}{$filename}?v={$md5}\"></script>";
} else {
$md5 = self::fileMd5($js);
if (stripos($js, ".min.") === false) {
$js = self::pareJs(JSMin::minify(file_get_contents(WEB_ROOT . $js)), dirname($js). '/' .basename($js, '.js') . '.min.js');
}
$jsstr = "<script src=\"{$cdn}{$js}?v={$mdt}\"></script>";
}
$tpl = str_replace($match[0][$k], $jsstr, $tpl);
}
return $tpl;
} static function parseCss($css_content, $filenme) { $css_content = preg_replace("/[\r\n\t]/", '', $css_content);
$css_content = preg_replace("/ +/", ' ', $css_content);
// echo WEB_ROOT . $filenme; sp_file_put_contents(WEB_ROOT . $filenme, $css_content); return $filenme;
} static function parseJs($js, $filenme) { sp_file_put_contents(WEB_ROOT . $filenme, $js); return $filenme; } function _loop($search, $tag, $arg) {
list($attr, $arg) = $this->parseAttr($arg);
$d = preg_split("/\s+/", trim($arg));
if (count($d) == 3) {
$data = $d[0];
$k = $d[1];
$v = $d[2];
$s = "<? \n if(!empty($data)) {\n \t foreach(" . $data . " as {$k} => {$v}){";
} elseif (count($d) == 2) {
$data = $d[0];
$v = $d[1];
$s = "<? \n if(!empty($data)){\n \t foreach(". $datsa . " as {$v}) {";
} if (isset($attr['counter'])) {
$s = "\n\t\t <? if (!isset({$attr['counter']})) { {$attr['counter']} = 0;} ?> \n \t\t" . $s . "\n \t\t{$attr['counter']}++; \n ?>";
} else {
$s .= "?>";
}
$this->string = str_replace($search, $s, $this->string);
$this->string = str_replace("{/loop}", "<? \t}\n}?>", $this->string);
}
function parseAttr($s) {
$reg = '/([a-zA-Z0-9_])+\s*=\s*([a-zA-Z0-9_\$]+)/i';
$arg = preg_replace($reg, '', $s);
preg_match_all($reg, $s, $d);
$arr = array();
foreach($d[1] as $key => $value) {
$arr[trim($value)] = trim($d[2][$key]);
}
return array($arr, $arg);
}
function _if ($search, $tag, $arg) {
$replace[0] = "<? if($arg){?>";
$replace[1] = "<? }?>";
$this->string = str_replace(array($search, "{/$tag}"), $replace, $this->string);
}
function _else($search, $tag, $arg) {
$this->string = str_replace("{else}", "\n" . '<? } else {?>', $this->string);
}
function _elseif($search, $tag, $arg) {
$this->string = str_replace($search, "<? }elseif($arg){?>", $this->string);
} function _for($search, $tag, $arg) {
$s = trim(trim(preg_replace('/\s+/', ' ; ', $arg)), ';');
$this->string = str_replace($search, "<? for($s){?>", $this->string);
$this->string = str_replace("{/for}", "<? \t\n}?>", $this->string);
}
function _while($search, $tag, $arg) {
$this->string = str_replace($search , "<? while($arg){ ?>", $this->string);
$this->string = str_replace("{/while}", "<? \t\n} ?>", $this->string);
}
function _end() {
//{$val}标签解析
$this->string = preg_replace('/\\{(\\$\\w+.*)\\}/is', "<?=\\\\1;?>", $this->string);
//{:$val()}标签解析
$this->string = preg_replace('/\\{\\:\\s*(\\$?\\w+.*?)\\}/is', "<? =\\\\1;?>", $this->string);
//将$val[arg]解析成$val['arg']
$this->string = preg_replace('/\\$([_a-z]+\\w*)\\[([_a-z]+\\w*)\\]/is', "$\\\\1['\\\\2']", $this->string);
//支持点号访问数组。如array['key']可以用array.key访问
$this->string = preg_replace('/\\$([_a-z]+\\w*)\\.(\\$[_a-z]+\\w*)/is', "$\\\\1[\\\\2]", $this->string);
$this->string = preg_replace('/\\$([_a-z]+\\w*)\\.([_a-z]+\\w*)/is', "$\\\\1['\\\\2']", $this->string);
$this->sring = preg_replace(array("/<\?/", "/<\?php\s*=/"), array("<?php ", "<?php echo "), $this->string);
}
function result() {
$this->_end();
$this->string = $this->_baseParse($this->string);
return str_replace(array("\\HKH1", "\\HKH2"), array("\\{", "\\}"), $this->string);
}
}
$data = ['aa','bb','cc'];
$bb = 'aa';
$con = 11;
$tag = new _HtmlTag('./index', '');
$str = file_get_contents('base.htm');
$tag->render($str);
$html = $tag->result();
var_dump($html);
echo '</br>----------------------------</br>';
sp_file_put_contents("./index.html", $html);
include "index.html";
- 运行index.php查看结果
总结 我们来总结一下这个类的使用过程,先实例话template类,调用template::display()方法,其中会调用template::compiles()方法,这个方法会实例化_HhtmlTag类,调用_HtmlTag::render方法,在这个方法里,对传入的带字符串进行一个结构的完整化,包括调用_HtmlTag::_extends,_HtmlTag::_import,_HtmlTag::_loop,_HtmlTag::_if,_HtmlTag::elseif,_HtmlTag::else,_HtmlTag::_for,_HtmlTag::while接着调用_HtmlTag::result方法,里面调用_HtmlTag::_end方法对变量进行php原生化和_HtmlTag::_baseParse()引入css和js,最后返回解析好的完整模板字符串。
php模板引擎的原理与简单实例的更多相关文章
- Vue.js双向绑定的实现原理和模板引擎实现原理(##########################################)
Vue.js双向绑定的实现原理 解析 神奇的 Object.defineProperty 这个方法了不起啊..vue.js和avalon.js 都是通过它实现双向绑定的..而且Object.obser ...
- PHP的模板引擎smarty原理浅谈
mvc是开发中的一个伟大的思想,使得开发代码有了更加清晰的层次,让代码分为了三层各施其职.无论是对代码的编写以及后期的阅读和维护,都提供了很大的便利. 我们在php开发中,视图层view是不允许有ph ...
- PHP的模板引擎smarty原理是什么(整理)
PHP的模板引擎smarty原理是什么(整理) 一.总结 一句话总结:其实所有的模板引擎的工作原理是差不多的,无非就是在php程序里面用正则匹配将模板里面的标签替换为php代码从而将两者混合为一个ph ...
- 高性能JavaScript模板引擎实现原理详解
这篇文章主要介绍了JavaScript模板引擎实现原理详解,本文着重讲解artTemplate模板的实现原理,它采用预编译方式让性能有了质的飞跃,是其它知名模板引擎的25.32 倍,需要的朋友可以参考 ...
- Win Socket编程原理及简单实例
[转]http://www.cnblogs.com/tornadomeet/archive/2012/04/11/2442140.html 使用Linux Socket做了小型的分布式,如Linux ...
- 浅谈dedecms模板引擎工作原理及其自定义标签
浅谈dedecms模板引擎工作原理: 理解织梦模板引擎有什么意思? 可以更好地自定义标签.更多在于了解织梦系统,理解模板引擎是理解织梦工作原理的第一步. 理解织梦会使我们写PHP代码是更顺手,同时能学 ...
- JavaScript 模板引擎实现原理解析
1.入门实例 首先我们来看一个简单模板: <script type="template" id="template"> <h2> < ...
- (转)浅谈dedecms模板引擎工作原理及自定义标签
理解织梦模板引擎有什么意义?一方面可以更好地自定义标签.更多在于了解织梦系统,理解模板引擎是理解织梦工作原理的第一步.理解织梦会使我们写php代码时更顺手,同时能学习一些php代码的组织方式. 这似乎 ...
- JS模板引擎handlebars.js的简单使用
handlebars.js的使用 首先我们要明白handlebars.js是干什么用的,我们知道使用jsp很麻烦, 于是我们开始使用freemarker等模板引擎,但这个只是针对服务器端来解 析的,那 ...
随机推荐
- svn 不能校验路径“XXX”的锁;没有匹配的可用锁令牌 故障解决方法
原文出自:https://blog.csdn.net/seesun2012 故障现象: svn Commit ,失败,提示: 不能校验路径"/SEESUN/****系统计划说明书.docx& ...
- JAVA线程池的原理分析
线程池的作用 1.降低资源的消耗 2.提高效率 3.方便管理 相关概念 corePoolSize核心线程数:核心池的大小,当有任务到达之后,就会创建一个线程去执行任务,当任务数量到达核心线程数后,就会 ...
- MongoDB 学习(三)MongoDB 和 Spring 整合(Maven)
一.MongoDB 和 Spring 整合(Maven) 1.相关 jar 包准备 2.用 Maven 创建项目,pom.xml 文件 <project xmlns="http://m ...
- thinkphp的删除操作
1.循环遍历要删除的用户的或者呀删除的文章的id值: <volist name="list" id="vo"> <tr id="si ...
- cookie实现记住登录名和密码
在最近学习的session作用域中,顺便了解了一下cookie, session是存放在服务器中,而cookie是存放在客户端中. 本篇文章主要是使用cookie来实现记住密码的功能. 简单的logi ...
- create-react-app找不到配置项
npm run eject 这个一个不可逆过程,一旦你执行了,就不能回到初始化
- Python基础-月考
1. 8<<2等于? # 解释:将8按位左移2位 # 8 0 0 0 0 1 0 0 0 # 32 0 0 1 0 0 0 0 0 2. 通过内置函数计算5除以2的余数 print(div ...
- Qt 日志输出文件
在Qt开发过程当中经常使用qDebug等一些输出来调试程序,但是到了正式发布的时候,都会被注释或者删除,采用日志输出来代替. 做过项目的童鞋可能都使用过日志功能,以便有异常错误能够快速跟踪.定 ...
- Java—进程与线程
进程与线程 进程是程序(任务)的执行过程,具有动态性:持有资源(共享内存.共享文件)和线程,是资源和线程的载体. 线程是系统中最小的执行单元,同一进程中有多个线程,线程共享进程的资源. 线程的交互,交 ...
- Azkaban调度器
Azkaban介绍 Azkaban 是由 Linkedin 公司推出的一个批量工作流任务调度器,用于在一个工作流内以一个特定的顺序运行一组工作和流程.Azkaban 使用 job 配置文件建立任务之间 ...