+ -
当前位置:首页 → 问答吧 → 【原创】php5开源模板项目 Temz Engine

【原创】php5开源模板项目 Temz Engine

时间:2008-11-14

来源:互联网

主要的技术是类似于解释器的取词方式解析
这是和大多数模板引擎不同的地方
svn://svnhost.cn/Temz
http://www.svnhost.cn/Project/Detail-711.shtml
内附实例
[php]
<?php     
/*
* Project: Temz, a simple template engine for php5
* Version: 1.1.28
*
* Author: IDGnarn <[email protected]>
* Copyright: 2007-2008 by idgnarn
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*/
define('_IN_Temz', 1);

class template {
public $conf = array(
  'temproot' => '/',
  'tplroot' => '/',
  'enablecahce' => false,
  'erron' => true,
  'debugon' => false,
);
private $_temz_vars = array(
  '_TEMZ_VER_' => '1.1.28',
  '_LANG_' => 'default',
);

function __construct($tplpath = '', $conf = array()){
  if(!empty($tplpath)){
   $this->load($tplpath, $conf);
  }
}

function load($tplpath, $confarray = array()){
  foreach($confarray as $confkey => $value){
   if($this->conf[$confkey] != $value && !empty($value)){
    $this->conf[$confkey] = $value;
   }
  }
        //原始文件路径
  $this->tplpath = $tplpath;
}

function getlang(){
  return $this->_temz_vars['_LANG_'];
}

function process($temporarypath){
  if(!$this->conf['enablecahce'] || !file_exists($temporarypath)){
         //编译模板
         $this->_process();
     }
}

function assign($varname, $value = ''){
        if (is_array($varname) && empty($value)){
            foreach ($varname as $name => $v){
                if (!empty($name)){
                    $this->_temz_vars[$name] = $v;
                }
            }
        } else {
            if (!empty($varname)){
                $this->_temz_vars[$varname] = $value;
            }
        }
}

function push($pushout = true){
  //临时文件路径
  $temporarypath = $this->conf['temproot'].'temporary-'.$this->getlang().'-'.md5(basename($this->tplpath)).'.php';
  
        if(!file_exists($this->conf['temproot'].'/')){
         $this->_halt(1, '临时文件夹不存在',"'{$this->conf['temproot']}'  不存在或无法写入");
  }
  if(!is_readable($this->tplpath)){
         $this->_halt(1, '模板载入错误', "'{$this->tplpath}' 文件不存在或无法读取");
        }
  
  if(!$this->conf['enablecahce'] || !file_exists($temporarypath)){
   $this->source = implode('', @file($this->tplpath));
   $this->_safecheck();
  }
  if($this->conf['enablecahce'] && file_exists($temporarypath)){
   if($pushout) require($temporarypath);
  }else{
   $this->process($temporarypath);
   $this->_writetofile($temporarypath, $this->_datafileheader().$this->source);
   if($pushout) require($temporarypath);
  }
}

//==========================================================================================

function _process(){
  //处理包含
  $this->_process_include();
  
  $this->_seek = 0;
  
  //str_split php5 only
  $this->source_array = str_split($this->source, 1);
  $this->_source_len = count($this->source_array);
  
  //取词
  do{
   $this->_get_token();
   $this->_handletoken($this->_token);
   
  }while(($this->_seek-1) != $this->_source_len);
  
  //处理转义
  $this->_process_trans();
}

function _halt($type, $message, $more = ''){
  $this->lasterrmsg = $message;
  switch($type){
   case '0':
    $typename = '编译时';
    break;
   case '1':
    $typename = '运行时';
    break;  
  }
  if($this->conf['erron']){
   die("<h4>{$typename}错误: {$message}<hr />更多: {$more}</h4>Powered by Temz {$this->_temz_vars['_TEMZ_VER_']}");
  }
}

function _datafileheader(){
  return
  "<?php\n/*\nTemz {$this->_temz_vars['_TEMZ_VER_']}\n".date('r')."\nCompiled From {$this->tplpath}\n*/\nif(!defined('_IN_Temz'))die('Access Denied');\n?>\n";
}

function _debugoutput($message){
  $this->lasterrmsg = $message;
  if($this->conf['debugon']){
   echo("<br />{$message}");
  }
}

function _safecheck(){
  $this->source = preg_replace("/<\s*\?(.*)\?\s*>/is", "", $this->source);
}
   
function _writetofile($filename, $source){
     $fp = fopen($filename, 'w');
     flock($fp, LOCK_EX);
     $file_source = fwrite($fp, $source);
     fclose($fp);
}

function _get_token(){
  //放弃使用正则取词,存在BUG和性能问题
   $temp = null;
   $type = null;
   $tagbegin = $tagend = false;
   $_source_array = $this->source_array;
   $_seek = $this->_seek;
   $_len = $this->_source_len;
   
   for($i=$_seek;$i<$_len;$i++){
    if(
     $i+4 < $_len &&  
     $_source_array[$i] == '<' &&
     $_source_array[$i+1] == '!' &&
     $_source_array[$i+2] == '-' &&
     $_source_array[$i+3] == '-' &&
     $_source_array[$i+4] == '{' &&
     !$tagbegin &&
     !$tagend
     )
    {
      $temp = null;
      $tagbegin = true;
      $type = 'comment';
    }elseif(
     $i+5 < $_len &&  
     $_source_array[$i] == '<' &&
     $_source_array[$i+1] == '!' &&
     $_source_array[$i+2] == '-' &&
     $_source_array[$i+3] == '-' &&
     $_source_array[$i+4] == '!' &&
     $_source_array[$i+5] == '{' &&
     !$tagbegin &&
     !$tagend
     )
    {
      $temp = null;
      $tagbegin = true;
      $type = 'comment-transferred';
    }elseif(
     $i+1 < $_len &&
     $_source_array[$i] == '!' &&
     $_source_array[$i+1] == '{' &&
     !$tagbegin &&
     !$tagend
     )
    {
      $temp = null;
      $tagbegin = true;
      $type = 'transferred';
    }elseif($_source_array[$i] == '{' && !$tagbegin){
      $temp = null;
      $tagbegin = true;
      $type = 'normal';
    }
   
    $temp .= $_source_array[$i];
    if($i+3 < $_len && $_source_array > $_source_array[$i] == '}' &&
     $_source_array[$i+1] == '-' &&
     $_source_array[$i+2] == '-' &&
     $_source_array[$i+3] == '>' && $tagbegin && !$tagend){
      $tagend = true;
      break;
   
    }elseif($_source_array[$i] == '}' && $tagbegin && !$tagend){
      $tagend = true;
      break;
    }elseif($_source_array[$i] == "\n"){
      $tagbegin = $tagend = false;
    }
   }
   
   
   
   $this->_seek = $i+1;
   if($type == 'comment' || $type == 'comment-transferred' && $tagend){
    $this->_tokenstr = $temp.'-->';
   }elseif($tagend){
    $this->_tokenstr = $temp;
   }else{
    $this->_tokenstr = null;
   }
   
   $this->_token = $this->_tokenstr;
   $this->_token = str_replace('<!--{', '', $this->_token);
   $this->_token = str_replace('<!--!{', '', $this->_token);
   $this->_token = str_replace('}-->', '', $this->_token);
   $this->_token = str_replace('!{', '', $this->_token);
   $this->_token = str_replace('{', '', $this->_token);
   $this->_token = str_replace('}', '', $this->_token);
   if($type == 'transferred' || $type == 'comment-transferred'){
     //转义
     $this->source = str_replace($this->_tokenstr, "{transferred:{$this->_token}}", $this->source);
     $this->source = str_replace($this->_tokenstr, '', $this->source);
     $this->_token = "transferred:{$this->_token}";
     $this->_tokenstr = "{transferred:{$this->_token}}";
   }
}
  
//处理开始==========================================================================================
function _handletoken($statement){
  $this->_debugoutput('<b>HandleToken:</b>'.htmlspecialchars($this->_tokenstr));
  //keyword(value):expression
  if(preg_match("/^(.+?)\((.+?)\)\:(.+?)$/",$statement, $matches)){
   $this->laststatement = $matches[1];
   
   switch ($this->laststatement) {
    case 'set':
     $this->_process_set($matches[2], $matches[3]);
     break;
    case 'foreach':
     $this->_process_foreach($matches[2], $matches[3]);
     break;
    case 'for':
     $this->_process_for($matches[2], $matches[3]);
     break;     
   }
  //keyword(expression)
  }elseif(preg_match("/^(.+?)\((.+?)\)$/",$statement, $matches)){
   $this->laststatement = $matches[1];
   
   switch ($this->laststatement) {
    case 'if':
     $this->_process_if($matches[2]);
     break;
    case 'foreach':
     $this->_process_foreach_noarg($matches[2]);
     break;
                case 'elseif':
                    $this->_process_if($matches[2], 'elseif');
                    break;                    
   }
  //keyword:expression
  }elseif(preg_match("/^(.+?)\:(.+?)$/",$statement, $matches)){
   $this->laststatement = $matches[1];
   
   switch ($this->laststatement) {
    case 'php':
     $this->_set($matches[2]);
     break;
    case 'phpvar':
     $this->_process_phpvar($matches[2]);
     break;
    case 'foreach':
     $this->_process_foreach_noarg($matches[2]);
     break;
    case 'if':
     $this->_process_if($matches[2]);
     break;
    case 'elseif':
     $this->_process_if($matches[2], 'elseif');
     break;
    case 'lang':
     $this->_process_lang($matches[2]);
     break;
    case 'definelang':
     $this->_process_definelang($matches[2]);
     break;
    case 'static':
     $this->_process_static($matches[2]);
     break;
    case 'var':
     $this->_process_vars($matches[2], 'global');
     break;
   }
  //keyword
  }else{
   $this->laststatement = $statement;
   
   switch ($this->laststatement) {
    case 'else':
     $this->_process_elsetag();
     break;
    case 'foreachelse':
     $this->_process_foreachelse();
     break;
    case '/if':
     $this->_process_endtag();
     break;
    case '/foreach':
     $this->_process_endtag_foreach();
     break;
    case '/for':
     $this->_process_endtag();
     break;
    default:
     $this->_process_vars($statement);
     break;
   }
  }
}
//处理结束==========================================================================================
  
//模块处理====================================================================================================
function _set($statement){
  $this->source = str_replace($this->_tokenstr, "<?php {$statement} ?>", $this->source);
}

function _process_endtag(){
  $this->_set('}');
}


function _process_trans(){
  $this->source = preg_replace("/(?:<\!--\s*|)\{transferred:(.+?)\}(?:\s*-->|)/is", "{\\1}", $this->source);
}

function _process_expression($expression, $type = 'runtime'){
  if($type == 'runtime'){
    $addition = '';
    preg_match_all("/(phpvar\:([\w\.]+))/", $expression, $matches);
    foreach($matches[2] as $id => $value){
     $token = $matches[1][$id];
     $value = trim($value);
     if(strpos($value, '.') > 0){
      $vname = substr($value, 0, strpos($value, '.'));
      $lname = $this->_process_dotvars(substr($value, strpos($value, '.')+1, strlen($value)), 0);
      $lname = "['{$lname}']";
      $expression = str_replace($token, "\${$vname}{$lname}", $expression);
      $addition .= "global \${$vname};\n";
     }else{
      $expression = str_replace($token, "\$$value", $expression);
      $addition .= "global \$$value;\n";
     }
    }
    preg_match_all("/(var\:([\w\.]+))/", $expression, $matches);
    foreach($matches[2] as $id => $value){
     $token = $matches[1][$id];
     $value = trim($value);
     $expression = str_replace($token, $this->_process_dotvars($value, 1), $expression);
    }
    $r[0] = $expression;
    $r[1] = $addition;
  }else{
    preg_match_all("/(phpvar\:([\w\.]+))/", $expression, $var_matches);
    foreach($var_matches[2] as $var_id => $var_value){
     if(strpos($var_value, '.') > 0){
      $vname = substr($var_value, 0, strpos($var_value, '.'));
      global $$vname;
      $thisvar = &$$vname;
      $subname  = substr($var_value, strpos($var_value, '.')+1, strlen($var_value));
      if(!is_array($thisvar)) $this->_halt(0, '变量不存在,无法继续编译(Temz::_process_include)', "{include:{$expression}}");
      $thisvar = $thisvar[$subname];
     }else{
      $vname = $var_value;
      global $$vname;
      $thisvar = &$$vname;
      if(is_array($thisvar)) $this->_halt(0, '变量不存在,无法继续编译(Temz::_process_include)', "{include:{$expression}}");
     }
     if(isset($thisvar)){
      $expression = str_replace($var_matches[1][$var_id], $thisvar, $expression);
     }else{
      $this->_halt(0, '变量不存在,无法继续编译(Temz::_process_include)', "{include:{$expression}}");
     }
    }
    preg_match_all("/(var\:([\w\.]+))/", $expression, $var_matches);
    foreach($var_matches[2] as $var_id => $var_value){
     if(strpos($var_value, '.') > 0){
      $vname = substr($var_value, 0, strpos($var_value, '.'));
      $subname  = substr($var_value, strpos($var_value, '.')+1, strlen($var_value));
      if(!is_array($this->_temz_vars[$vname])) $this->_halt(0, '变量不存在,无法继续编译(Temz::_process_include)', "{include:{$expression}}");
      $thisvar = $this->_temz_vars[$vname][$subname];
     }else{
      if(is_array($this->_temz_vars[$var_value])) $this->_halt(0, '变量不存在,无法继续编译(Temz::_process_include)', "{include:{$expression}}");
      $thisvar = $this->_temz_vars[$var_value];
     }
     if(isset($thisvar)){
      $expression = str_replace($var_matches[1][$var_id], $thisvar, $expression);
     }else{
      $this->_halt(0, '变量不存在,无法继续编译(Temz::_process_include)', "{include:{$expression}}");
     }
    }
                $r = $expression;
  }
  return $r;
}

function _process_include(){
  preg_match_all("/((?:<\!--\s*#\s*|)\{include(?:\:|\()(.+?)(?:\)|)\}(?:\s*#\s*-->|))/", $this->source, $matches);
  foreach($matches[2] as $id => $value){
   $expression = $value;
   $token = $matches[1][$id];
   $expression = $this->_process_expression($expression, 'compile');
   if(isset($expression) && file_exists($this->conf['tplroot'].$expression)){
    $this->source = str_replace($token, implode('', @file($this->conf['tplroot'].$expression)), $this->source);
   }elseif(isset($expression)){
    $this->_halt(0, '文件不存在,无法继续编译(Temz::_process_include)', "{include:{$this->conf['tplroot']}{$expression}}");
   }  
        }
}
  
  function _process_foreach($from, $to){
   $from = $this->_process_dotvars($from);
   $to = $this->_process_dotvars($to);
   $this->_set("if(!empty({$from}) && is_array($from)){ foreach ({$from} as \$this->_temz_vars['_key_'] => {$to}){\$this->_temz_vars['_item_'] = {$to}");
  }
  
  function _process_foreach_noarg($from){
   $from = $this->_process_dotvars($from);
   $this->_set("if(!empty({$from}) && is_array($from)){ foreach ({$from} as \$this->_temz_vars['_key_'] => \$this->_temz_vars['_item_']){");
  }
  
function _process_endtag_foreach(){
  $this->_set('}}');
}

function _process_foreachelse(){
  $this->_set('}}else{{');
}
  
function _process_if($expression, $type = 'if'){
  $expression = $this->_process_expression($expression, 'runtime');
  if($type == 'if'){
   $this->_set("{$expression[1]}if({$expression[0]}){");
  }else{
   $this->_set("{$expression[1]}}elseif({$expression[0]}){");
  }
}

function _process_elsetag(){
  $this->_set('}else{');
}

function _process_static($varname, $type=1){
  if(strpos($varname, '.') > 0){
   $vname = substr($varname, 0, strpos($varname, '.'));
  }else{
   $vname = $varname;
  }
  $subname  = substr($varname, strpos($varname, '.')+1, strlen($varname));
  if($type){
   global $$vname;
   $thisvar = &$$vname;
   if(is_array($thisvar)) $thisvar = $thisvar[$subname];
  }else{
   $thisvar = $this->_temz_vars['_LANGVAR_'][$this->getlang()][$varname];
  }
  if(isset($thisvar)){
   $this->source = str_replace($this->_tokenstr, $thisvar, $this->source);
  }else{
   $this->_halt(0, '静态变量不存在,无法继续编译(Temz::_process_static)', "{static:{$varname}}");
  }
}

function _process_lang($varname){
  $this->_process_static($varname, 0);
}

function _process_phpvar($varname){
  $r = $this->_process_expression("phpvar:{$varname}", 'runtime');
  $expression = $r[0];
  $addition = $r[1];
  $this->_set("{$addition}echo {$expression};");
}

function _process_definelang($varname){
  preg_match('/^(?:\$|)(.*?)$/', $varname, $matches);
  $this->_langvar = $matches[1];
  $this->source = str_replace($this->_tokenstr, '', $this->source);
}

function _process_set($varname, $value){
  if(preg_match("/^'(.*)'$/", $value, $matches)){
   $this->_temz_vars[$varname] = $matches[1];
   $this->source = str_replace($this->_tokenstr, "<?php \$this->_temz_vars['{$varname}'] = '{$matches[1]}' ?>", $this->source);
  }else{
   $this->source = str_replace($this->_tokenstr, "<?php \$this->_temz_vars['{$varname}'] = \$this->_temz_vars['{$value}'] ?>", $this->source);
  }
}

function _process_vars($varname){
  if(!empty($varname)){
   $_replace_tokenstr = '/'.str_replace('/', '\/' , preg_quote($this->_tokenstr)).'/';
   $this->source = preg_replace($_replace_tokenstr, "<?php echo \$this->_temz_vars['{$this->_process_dotvars($varname, 0)}']; ?>", $this->source, 1);
  }
}

function _process_dotvars($v, $type=1){
  if($type == 1){
   $v = str_replace('.', "']['", $v);
   $v = "\$this->_temz_vars['{$v}']";
  }elseif($type == 2){
   $v = str_replace('.', "['", $v);
   $v = "{$v}']";
  }else{
   $v = str_replace('.', "']['", $v);
  }
  return $v;
}

function _process_for($varname, $expression){
        $expressions = explode(',', $expression);
        $first = $this->_process_expression($expressions[0]);
        $end = $this->_process_expression($expressions[1]);
        $step = $this->_process_expression($expressions[2]);
        $this->_set("{$first[1]}{$end[1]}{$step[1]}for(\$this->_temz_vars['{$varname}']={$first[0]};\$this->_temz_vars['{$varname}']<={$end[0]};\$this->_temz_vars['{$varname}']+={$step[0]}){");
    }
}
?>
[/php]

作者: idgnarn   发布时间: 2008-11-14