我常用的一个非常好的生成静态页面的模板处理类Template_class

发布时间:2008年03月04日      浏览次数:3676 次
<?php
//呵呵,我的英文不是很好,下面很多注释不一定正确,懂英文的就自已看英文注释吧,也许更容易看懂
class Template_class
{
      /**
       * When is set to true it makes the class clean the loops and cloops data
       * The value of this variable can be set while creating the object 当创建对象的时候,这个变量的值可以被设置
       *
       * @var bool
       * @access public
       */
      var $reset = TRUE;
      /**
       * The beginning of the normal tags
       *
       * @var string
       * @access public
       */
      var $ldelim = '{'; //正规标签始端
      
      /**
       * The end of the normal tags
       *
       * @var string
       * @access public
       */
      var $rdelim = '}';//正规标签末端
      /**
       * The beginning of the opening tag for blocks
       *
       * @var string
       * @access public
       */
      var $BAldelim = '{';//开始标签始端
      /**
       * The end of the opening tag for blocks
       *
       * @var string
       * @access public
       */
      var $BArdelim = '}';//开始标签末端
      /**
       * The beginning of the closing tag for blocks
       *
       * @var string
       * @access public
       */
      var $EAldelim = '{/';//结束标签始端
      /**
       * The end of the closing tag for blocks
       *
       * @var string
       * @access public
       */
      var $EArdelim = '}'; //结束标签末端
      /**
       * Array containing the data for the tag: tags
       *
       * @var array
       * @access private
       */
      var $_scalars = array();//tag:标签 数组
      /**
       * Array containing the data for the loop: tags
       *
       * @var array
       * @access private
       */
      var $_arrays = array();//loop:标签 数组
      /**
       * Array containing the information about which array tags
       * should be enabled for if: tags and which elements should be used
       * since trying to find ifs for each array element and
       * for each array entry would take a lot of time.
       *
       * @var array
       * @access private
       */
      var $_arrays_ifs = array();
      /**
       * Array containing the data for the cloop: tags
       *
       * @var array
       * @access private
       */
      var $_carrays = array();//cloop:标签 数组
      /**
       * Array containing the data for the if: tags
       *
       * @var array
       * @access private
       */
      var $_ifs = array();//if:标签 数组
      /**
       * Array containing the information of the plugins to be loaded
       *
       * @var array
       * @access private
       * @see $this->add_plugin
       */
      var $_plugins = array();
      /**
       * Object constructor, set $reset to reset the arrays after processing 对象构造,设置$reset变量在处理后重新初始化数组
       *
       * @access public
       * @param bool $reset
       * @return Template_class
       */
      function Template_class($reset = TRUE)
      {
            $this->reset = $reset;
      }
      /**
       * Set a tag with a value, $var can be a string or an array 设置一个标签和一个值,$var变量可以是一个字符串或者一个数组
       * Set $if to true if you want to be able to use that tag with if: conditions
       *
       * @param string $tag
       * @param mixed $var
       * @param bool $if
       */
      function set($tag, $var, $if = NULL)
      {
            if(is_array($var))
            {
                  $this->_arrays[$tag] = $var;
                  if($if!==null)
                  {
                        if(!is_array($if))
                        {
                              $result = $var ? TRUE : FALSE;
                              $this->_ifs[] = $tag;
                              $this->_scalars[$tag] = $result;
                        }
                        else
                        {
                              $result = $var ? TRUE : FALSE;
                              $this->_ifs[] = $tag;
                              $this->_scalars[$tag] = $result;
                              $this->_arrays_ifs[$tag]=$if;
                        }
                  }
            }
            else
            {
                  $this->_scalars[$tag] = $var;
                  if($if) $this->_ifs[] = $tag;
            }
      }
      /**
       * Sets a case loop
       *
       * @access public
       * @param string $tag The tag name
       * @param array $array The array containing the data
       * @param array $cases The array telling about the cases that it should check for
       */
      function set_cloop($tag, $array, $cases)
      {
            $this->_carrays[$tag] = array(
            'array' => $array,
            'cases' => $cases);
      }
      /**
       * Reset the template variables 重设模板变量
       *
       * @access public
       * @param bool $scalars If the scalars data should be cleaned
       * @param bool $arrays If the arrays data should be cleaned
       * @param bool $arrays_ifs If the arrays_ifs data should be cleaned
       * @param bool $carrays If the case arrays data should be cleaned
       * @param bool $ifs If the ifs data should be cleaned
       * @param bool $plugins If the plugins data should be cleaned
       */
      function reset($scalars=false, $arrays=false, $arrays_ifs=false, $carrays=false, $ifs=false,$plugins=false)
      {
            if($scalars) $this->_scalars = array();
            if($arrays) $this->_arrays = array();
            if($arrays_ifs) $this->_arrays_ifs = array();
            if($carrays) $this->_carrays = array();
            if($ifs) $this->_ifs = array();
            if($plugins) $this->_plugins = array();
      }
      /**
       * Formats the tags & returns a two-element array, 格式化标签并且返回一个两个元素的数组
       * the opening and closing tags 开始与结束标签
       *
       * @access public
       * @param string $tag The tag name
       * @param string $directive The directive name
       * @return array
       */
      function get_tags($tag, $directive)
      {
            $tags['b'] = $this->BAldelim . $directive . ':' . $tag . $this->BArdelim; //如{
            $tags['e'] = $this->EAldelim . $directive . ':' . $tag . $this->EArdelim;
            return $tags;
      }
      /**
       * Formats a simple tag
       *
       * @access public
       * @param string $tag The tag name
       * @param string $directive The tag directive
       * @return string The formated tag
       */
      function get_tag($tag,$directive='tag')
      {
            return $this->ldelim . $directive . ':' . $tag . $this->rdelim;
      }
      /**
       * Extracts a portion of a template( or a string) according to the
       * opening ($tags['b']) and closing ($tags['e']) tags
       *
       * @param array $tags The opening and closing tags/delimeters
       * @param string $contents The content from where it is going to extract
       * @return string The extracted content
       */
      function get_statement($tags, $contents)
      {
            // Locate the statement
            $tag_length = strlen($tags['b']);
            $fpos = $tags['b']===null? 0 : strpos($contents, $tags['b']);
            $lpos = $tags['e']===null? strlen($contents) : strpos($contents, $tags['e']);
            if($fpos===false||$lpos===false)
            return false;
            $fpos += $tag_length;
            $length = $lpos - $fpos;
            // Extract & return the statement
            return substr($contents, $fpos, $length);
      }
      /**
       * Parse the template with the variables 解析模板变量
       *
       * @access public
       * @param string $contents The template
       * @return string The parsed template
       */
      function parse($contents)
      {
            /**
             * Process the ifs (if any)
             */
            foreach($this->_ifs as $value)
            {
                  $contents = $this->_parse_if($value, $contents);
            }
            /**
             * Process the scalars (if any)
            */
            foreach($this->_scalars as $key => $value)
            {
                  $contents = str_replace($this->get_tag($key), $value, $contents);
            }
            /**
             * Process the arrays (if any)
             */
            foreach($this->_arrays as $key => $array)
            {
                  $contents = $this->_parse_loop($key, $array, $contents);
            }
            /**
             * Process the case arrays (if any)
             */
            foreach($this->_carrays as $key => $array)
            {
                  $contents = $this->_parse_cloop($key, $array, $contents);
            }
            $plugins_count=count($this->_plugins);
            /**
             * Process the plugins (if any)
             */
            for ($n=0;$n<$plugins_count;$n++)
            {
                  if($this->_plugins[$n]['object']===null)
                  $obj=new $this->_plugins[$n]['function']();
                  else
                  $obj=&$this->_plugins[$n]['object'];
                  //
                  //Now we check what protocol version the plugin was designed for
                  //
                  switch ($obj->version('protocol'))
                  {
                        case "1.0":
                              {
                                    //
                                    //Now we give the object itself to the function, so it can access the current settings
                                    //
                                    $contents=$obj->parse($contents,$this);
                              }break;
                  }
            }
            /**
             * Reset the data according to the settings
             */
            if($this->reset) $this->reset(FALSE, TRUE, TRUE, TRUE);
            return $contents;
      }
      /**
       * Parses an if statement //解析一个if...esle...语句,示例:{if:标签}...{else:标签}...{/if:标签}
       *
       * @access private
       * @param string $tag The tag name
       * @param string $contents The current as-processed template
       * @param string $replace If the function should consider as $replace without checking the real value
       * @return string The parsed template
       */
      function _parse_if($tag, $contents, $replace=null)
      {
            //
            // Get the tags
            //
            $t = $this->get_tags($tag, 'if'); //返回一个$t数组,值分别为:$t['b']="{if:标签$tag}",$t['e']="{/if:标签$tag}",如:{if:test}{/if:test}
            //
            //We loop this so we can process all the ifs for this tag
            //
            while (($entire_statement = $this->get_statement($t, $contents))!==false)
            {
                  // Get the else tag 获取else标签
                  $tags['b'] = NULL;
                  $tags['e'] = $this->get_tag($tag, 'else');
                  // See if there's an else statement 判断是否有else语句
                  if(($else = strpos($entire_statement, $tags['e'])))
                  {
                        // Get the if statement
                        $if = $this->get_statement($tags, $entire_statement);
                        // Get the else statement
                        $else = substr($entire_statement, $else + strlen($tags['e']));
                  }
                  else
                  {
                        $else = NULL;
                        $if = $entire_statement;
                  }
                  //
                  //If the function wasn't called with a value for $replace we check the _scalars array
                  //
                  if($replace===null||!is_bool($replace))
                  $replace=!empty($this->_scalars[$tag])?true:false;
                  //
                  //If the condition is valid then we use the 'if' (first) part, if not, then we use 'else'
                  //
                  $replace=($replace) ? $if : $else;
                  //
                  // Parse the template
                  //
                  $contents = str_replace($t['b'] . $entire_statement . $t['e'], $replace, $contents);
            }
            //
            //Return the template
            //
            return $contents;
      }
      /**
       * Parses a loop //解析一个loop语句,示例:{loop:标签}...{/loop:标签}
       *
       * @access private
       * @param string $tag Tag name
       * @param array $array The array containing the loop data
       * @param string $contents The current as-processed template
       * @return string The parsed template
       */
      function _parse_loop($tag, $array, $contents)
      {
            // Get the tags & loop
            $t = $this->get_tags($tag, 'loop');
            while (($loop = $this->get_statement($t, $contents))!==false)
            {
                  $parsed = NULL;
                  $if_key_exists=isset($this->_arrays_ifs[$tag]);
                  // Process the loop
                  foreach($array as $key => $value)
                  {
                        /**
                         * We create a copy of the loop so we can keep the original loop
                         * but work on this one
                         */
                        $i = $loop;
                        if($if_key_exists&&isset($this->_arrays_ifs[$tag][$key]))
                        $i=$this->_parse_if($tag . '.' . $key,$i,!empty($value)?true:false);
                        /**
                         * array(1=>array('key_name'=>'value','some_key'=>'value'))
                         * {tag:tag_name[].key_name},{tag:tag_name[].some_key}
                         * {tag:tag_name[].key_name[]},{tag:tag_name[].some_key[].some_subkey}
                         */
                        if(is_numeric($key) && is_array($value))
                        {
                              foreach($value as $key2 => $value2)
                              {
                                    if($if_key_exists&&isset($this->_arrays_ifs[$tag][$key2]))
                                    $i=$this->_parse_if($tag . '[].' . $key2,$i,!empty($value2)?true:false);
                                    if(!is_array($value2))
                                    {
                                          // Replace associative array tags
                                          $i = str_replace($this->get_tag($tag . '[].' . $key2), $value2, $i);
                                    }
                                    else
                                    {
                                          // Check to see if it's a nested loop
                                          $i = $this->_parse_loop($tag . '[].' . $key2, $value2, $i);
                                    }
                              }
                        }
                        /**
                         * array('tsgsgs'=>'sgsgdgg')
                         * {tag:tag_name.key_name}
                         */
                        elseif(is_string($key) && !is_array($value))
                        {
                              $i = str_replace($this->get_tag($tag . '.' . $key), $value, $i);
                        }
                        /**
                         * array(1=>'fff')
                         * {tag:tag_name[]}
                         */
                        elseif(!is_array($value))
                        {
                              $i = str_replace($this->get_tag($tag . '[]'), $value, $i);
                        }
                        // Add the parsed iteration
                        if(isset($i)) $parsed .= rtrim($i);
                  }
                  //
                  // Parse the template
                  //
                  $contents=str_replace($t['b'] . $loop . $t['e'], $parsed, $contents);
            }
            //
            //Return the template
            //
            return $contents;
      }
      /**
       * Parse a case loop 解析一个case循环语句,相当于switch一样
       *
       * @access private
       * @param string $tag The tag name that is going to be parsed
       * @param array $array Array with the loop elements
       * @param string $contents The current as-processed template
       * @return string The parsed template
       */
      function _parse_cloop($tag, $array, $contents)
      {
            // Get the tags & loop
            $t = $this->get_tags($tag, 'cloop'); //返回{cloop:名称}{case:名称}...{default:名称}{/cloop:名称}
            while (($loop = $this->get_statement($t, $contents))!==false)
            {
                  // Set up the cases
                  $array['cases'][] = 'default';
                  $case_content = array();
                  $parsed = NULL;
                  // Get the case strings
                  foreach($array['cases'] as $case)
                  {
                        $ctags[$case] = $this->get_tags($case, 'case');
                        $case_content[$case] = $this->get_statement($ctags[$case], $loop);
                  }
                  // Process the loop
                  foreach($array['array'] as $key => $value)
                  {
                        if(is_numeric($key) && is_array($value))
                        {
                              // Set up the cases
                              if(isset($value['case'])) $current_case = $value['case'];
                              else $current_case = 'default';
                              unset($value['case']);
                              $i = $case_content[$current_case];
                              // Loop through each value
                              foreach($value as $key2 => $value2) {
                                    $i = str_replace($this->get_tag($tag . '[].' . $key2), $value2, $i);
                              }
                        }
                        // Add the parsed iteration
                        $parsed .= rtrim($i);
                  }
                  // Parse & return the final loop
                  $contents=str_replace($t['b'] . $loop . $t['e'], $parsed, $contents);
            }
            return $contents;
      }
      /**
       * Parses the file $file_name as a template
       *
       * @access public
       * @param string $file The template file name
       * @return string The processed template
       */
      function fetch($file,$filename = '')
      {
            // Open the file
            $fp = fopen($file, 'rb');
            if(!$fp) return FALSE;
            // Read the file
            $contents = fread($fp, filesize($file));
            // Close the file
            fclose($fp);
            // Parse and return the contents
            
            if (empty($filename)){
            
                  /*------------------------------------*/
                  //动态浏览
                  return $this->parse($contents);
                  /*------------------------------------*/
            
            }else{
            
                  /*------------------------------------*/
                  //生成静态页面
                  $content = $this->parse($contents);
                  
                  $handle = fopen ($filename,"w"); //打开文件指针,创建文件
                  /*
                   检查文件是否被创建且可写
                  */
                  if (!is_writable ($filename)){
                  //die ("文件:".$filename."不可写,请检查其属性后重试!");
                  }
                  if (!fwrite ($handle,$content)){ //将信息写入文件
                  //die ("生成文件".$filename."失败!");
                  }
                  fclose ($handle); //关闭指针
                  /*------------------------------------*/
            
            }
      }
      /**
       * Works the same way as set() excepting that if the tag already exists //除了if标签已经存在以外,功能与set()一样
       * it doesn't replaces it, it appends the new value (if it is an string)
       * or it merges the content (if it is an array). //append()不能代替set(),append() 添加一个新值(如果这个值是一个字符串),或者添加一个元素(如果它是一个数组)
       * Please note that this function will not make any change
       * to the array_ifs data, to update that information,//请注意这个函数对array_ifs数据更新信息不会产生任何改变
       * set() has to be used instead //set()函数则一定会替换旧的值
       *
       * @param string $tag
       * @param mixed $var
       * @param bool $if
       */
      function append($tag, $var, $if = NULL)
      {
            if(is_array($var))
            {
                  if(!isset($this->_arrays[$tag]))
                  $this->_arrays[$tag]= $var;
                  else
                  $this->_arrays[$tag]=array_merge($this->_arrays[$tag],$var);
                  if($if)
                  {
                        $result = $var ? TRUE : FALSE;
                        $this->_ifs[] = $tag;
                        $this->_scalars[$tag] = $result;
                  }
            }
            else
            {
                  if(!isset($this->_scalars[$tag]))
                  $this->_scalars[$tag] = $var;
                  else
                  $this->_scalars[$tag] .= $var;
                  if($if) $this->_ifs[] = $tag;
            }
      }
      /**
       * Adds a plugin to be called when a template is being parsed
       * The $plugin_name is the name of the class which is the plugin
       *
       * The $setup var may contain any type of data, because it is pased directly to the $plugin_name::setup() function of the plugin class
       * The arguments passed to the $plugin_name::parse() function will be $contents and the object itself ($this), in that order, and the function will only return the parsed template.
       *
       * The $plugin_name::setup() function may return an array with some settings
       *
       * Notes:
       *       The plugin class must have the next functions, which are going to be called by the template engine:
       *             -$plugin_name::setup() This function can be used to setup either the template engine when calling the plugin or to setup the plugin itself, in the last case, the $setup param can be used to give some settings to the plugin
       *             -$plugin_name::parse() This function is called when the class 'executes' the plugin
       *             -$plugin_name::version() This function must have one argument, which specifies the version of what is being requested: 'protocol' is the current plugins protocol, for the current version is 1.0; 'plugin' is the plugin version, it can be useful to debug
       *
       * @access public
       * @param string $plugin_name The name of the function or class to be called
       * @param mixed $setup Special settings that can be given to the plugin
       */
      function add_plugin($plugin_name,$setup=null)
      {
            $n=count($this->_plugins);
            $this->_plugins[$n]['function']=$plugin_name;
            $this->_plugins[$n]['setup']=$setup;
            $obj=new $plugin_name();
            $this->_plugins[$n]=array_merge($this->_plugins[$n],$obj->setup($setup));
            if (!isset($this->_plugins[$n]['refresh_object'])||$this->_plugins[$n]['refresh_object']===false)
            $this->_plugins[$n]['object']=&$obj;
            else
            $this->_plugins[$n]['object']=null;
      }
}
?>
=================================
示例
=================================
将以上代码保存到Template_class.php文件中.看不看得懂并不重要,只要看懂以下代码学会用就可以了,以下主要的代码我都加在后面******号,其它的代码可有可无,自已写就得了,灵活运用
--------------------------------
调用类的测试页 test.php 相关代码
--------------------------------
<?php
include ("conn.php");//创建数据库链接
include ("mysql_func.php");//自定义的一个数据库处理页面
require_once("Template_class.php");//******这是上面的模板处理类页面
$tpl=new Template_class();//******创建对象
$sql = "select * from test ";
genpage($sql,15);//这是一个分页函数,具体代码在mysql_func.php里,统计总页数,并返回一个$sql语句,以便下面这行使用,15为每页显示的记录数
$result = mysql_query($sql);
while($rs =mysql_fetch_array($result))
{
      $test[] = array("str"=>"$rs[str]");//******这个数组的名称很重要,也可以自定义,但要与下面set里的参数$test名称相同,最好三个参数的名称都一样,如下面的test,如果页面里不用显示数据列表的话,此数组可省略,str为数据库表里的一个字段名称
//上面这里只以显示一个字段为例,如果有多个字段则$test[] = array("str"=>"$rs[str]","字段名称"=>"值",...);
}
//******以下set()函数比较重要,主要是将模板面里的相关标签替换为相关值,$tpl->set("标签名称","要替换的值");
$tpl->set("test",$test,array("test"=>true));//将数据替换到静态模板页相关位置,注意后面的array("test"=>true)这个参数,一般是显示数据列表时用到
$tpl->set("showpage",showpage());//******将分页代码插入静态模板页的指定位置
$tpl->set("title","测试调用模板处理类");//******如果只是一个字符串的话,可以这样子
//...根据模板页面设置好的标签多少在此增加或减少$tpl->set()
die($tpl->fetch("mode.html"));//******将数据填充入静态模板页mode.html并直接显示出来,不生成静态页面
//如果需要生成静态页面的话,那么上一句这样写即可,die($tpl->fetch("模板页的路径及名称","要生成页面的路径及名称"));如die($tpl->fetch("mode.html","/1/1.html"));
      
?>
--------------------------------
静态模板页 mode.html 相关代码
--------------------------------
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<table>
<tr><td>{tag:title}<!--这里就是上面那个tpl->set("title","测试调用模板处理类");要替换的位置--></td></tr>
<!--下面开始循环显示记录,下面的那个:号左边的标签test可以改变,但要对应正确-->
{if:test}
{loop:test}
<tr>
<td align="center">{tag:test[].str}</td>
<!--
如果有更多字段的话
<td align="center">{tag:test[].str2}</td>
<td align="center">{tag:test[].str3}</td>
...
-->
</tr>
{/loop:test}
{else:test}
<tr>
<td height="100" align="center">没有数据</td>
</tr>
{/if:test}
</table>
<table width="100%" border="0" cellpadding="0" cellspacing="0">
<tr>
<td>{tag:showpage}<!--这里是分页处--></td>
</tr>
</table>
</body>
</html>
ok,就说到这了,其它的就自已去研究与测试吧,呵呵
文章来源:桂林唯创网络
免责声明:本站相关技术文章信息部分来自网络,目的主要是传播更多信息,如果您认为本站的某些信息侵犯了您的版权,请与我们联系,我们会即时妥善的处理,谢谢合作!