PHP下一个方便易用的模板处理类

Title : 一个方便易用的小模板处理类
Author : Stangly Wrong



手册地址: http://blog.csdn.net/stangly/archive/2004/12/08/209102.aspx

在PHP中使用模板技术,一直是一个受欢迎的技术。自从2001年开始,PHP的很多爱好者在PHP中引用了MVC开发模式后,模板技术就更为火热。

网上很多出名的模板引擎,如phplib的template、FastTemplate、easyTPL、BTemplate以及现在备受php.net和众多爱好者推宠的Smarty模板引擎。

我个人非常喜欢那些短小精悍的php代码,像Smarty这些动折2000多行的代码实现的模板引擎,不得不令我望而生畏。

前一段时间,看了phpe.net上有一篇文章叫<<超越模板引擎>>其作者Brian Lozier所说确实极其有道理。他这样说:Smarty的目标是"把业务逻辑从表现中分离出来"而不是"PHP代码和HTML代码的分离"。但是Smarty确实也太庞大了,如果我在使用Smarty的话,我会感觉Smarty会不会影响php代码执行效率,首先在解析2000多行的Smarty代码就可能会带来很大延迟。虽然他采用编译技术,但是程序在运行时还是需要将Smarty这个引擎include进来,php脚本引擎每次都会去解析这个庞大的代码。想想也确实可怕。

Brian Lozier影响,我体会到php本身就是一个嵌入式的脚本语言,如果我们能够使用用简单的php代码写成的模板,而在运行时直接将他include进来,那不是更为快捷?结果也正是我猜想一样。我在用Brian Lozier的类测试后,确实比Smarty快50%左右。

像下面的模板标记代码
My name is {myname}.
为什么我们不直接写成
My name is <?=$myname?>.
这样样子呢?

还有模板里常用的列表,其实也是简单的loop

<!-- BEGIN user_list -->
User ID      : {var0}
User Name : {var1}
<!-- END user_list -->

为什么我们不直接写成如下的代码

<?php foreach ( $user_list as $user ) {  ?>
    User ID : <?=['id']?>
    User Name : <?=['name']?>
<?php
} ?>

早在一年前,我就尝试用这种方法去做一些企业和政府的WebOS,只是当时,还没有意识这样做带来的好处。只是感觉这样的模板我不需要单独的代码去处理他,只是简单的include一下就ok了。而且,我记得当时在做这些用php语言直接写出的模板的时候,非常快捷,以至于到后来,参与开发几个美工都因此对php的程序开发,发生了很大的兴趣,开始学习php的脚本语言。

最近又受<<超越模板引擎>>一文影响,所以决定根据他的思想去实现一个模板处理的类,经过了二三天的每天像榨水果汁一样挤出的几个小时去完成了一个模板类。现在张贴在此,以便能有志同道合朋友能够与我一起讨论。

考虑了现在带会经常使用{name}的这样的标记去做模板,所以我写一个简单的Compile()的方法,去解析这样的文件,将其转换为<?=$name?>这样的php模板文件。然后将转换后的文件存贮到相应的cache目录中去,然后提供一个_Parse()方法去include文件然后在主程序中echo输出。在实现的时候,我为了简化调用方法.使用了一个Parse()方法去呼叫上述两个私有方法.

以下是代码示例:

首先给出目录结构:
 /  根目录
├─cache           存放php模板缓存的文件
├─includes        类库存放目录
│  └─template    模板处理template.php类库文件存放目录
└─templates       模板目录
    ├─imgs           模板文件的图片目录
    │  └─ver.1
    └─tpls           模板文件目录
        └─ver.1

示例一:对于html类型的模板文件的处理.
示例一:对于html类型的模板文件的处理.
test.htm.php 模板文件存放在 /templates/tpls/ver.1/目录下
<html>

<head>
    <title>{title}</title>
</head>

<body>
<!-- Trim php code that can't occur in HTML type template file. Now , that will be disable . -->
<?
print_r($_ENV);
?>
<?=$test?>
<? if ( $a == $b ) echo 'a==b'; ?>

<!--IF condition-->
<table border="1" align="center">
    <tr>
        <td>$condition is TRUE</td>
    </tr>
</table>
<!--ENDIF-->

<!--IF a == b-->
<table border="1" align="center">
    <tr>
        <td>$a == $b</td>
    </tr>
</table>
<!--ENDIF-->

<!--IF a != b-->
<table border="1" align="center">
    <tr>
        <td>$a != $b</td>
    </tr>
</table>
<!--ENDIF-->

<table border="1" align="center">
    <tr>
        <td>id</td>
        <td>name</td>
    </tr>

    <!--LIST user_list AS user-->
    <tr>
        <td>{user[id]}</td>
        <td>{user[name]}</td>
    </tr>
    <!--ENDLIST-->
</table>

</body>

</html>

test_htm.php文件对类库调用示例,本文件存放在 / 目录下
下面是对这样的html类型的模板出理调用的具体过程.

<?
require_once('includes/template/template.php');

$tpl = new Template;
$tpl->SetFile('templates/tpls/ver.1/test.htm.php', TPL_HTM);

// 对于htm类型模板来说,通过此设置编译后的模板文件
$tpl->SetCacheDir('cache/');

$tpl->SetVar('title','I love jear');
$tpl->SetVar('condition',TRUE);
$tpl->SetVar('a','a');
$tpl->SetVar('b','a');

$user_list = array(
    array(
        
'id' => '1',
        
'name' => 'Stangly.wrong'
    
),
    array(
        
'id' => '2',
        
'name' => 'Jear'
    
),
);
$tpl->SetVar('user_list',$user_list);

echo
$tpl->Parse();

?>

观察一下目录结构的变化
/
├─cache
│  └─templates
│      └─tpls
│          └─ver.1
├─includes
│  └─template
└─templates
    ├─imgs
    │  └─ver.1
    └─tpls
        └─ver.1

程序会自动在cache目录下,建立与template文件存放的路径相同的目录路径去存放编译后的模板文件。这样个人感觉非常便于查找与排错。

示例二:对于php类型的模板文件的处理,观察一下与htm类型的模板文件有什么不同?是不是所有 {title} 这样的标记都被替换为 <?=$title?>这种形式,以及对于所谓的 block 也被直接替换为 foreach() 方式。

test.php.php 模板文件存放在 /templates/tpls/ver.1/目录下

<html>

<head>
    <title><?=$title?></title>
</head>

<body>

<? if ($condition) { ?>
<table border="1" align="center">
    <tr>
        <td>$condition is TRUE !</td>
    </tr>
</table>
<? } ?>

<? if ($a == $b) { ?>
<table border="1" align="center">
    <tr>
        <td>$a == $b</td>
    </tr>
</table>
<? } ?>

<? if ($a != $b) { ?>
<table border="1" align="center">
    <tr>
        <td>$a != $b</td>
    </tr>
</table>
<? } ?>

<table border="1" align="center">
    <tr>
        <td>id</td>
        <td>name</td>
    </tr>

    <? foreach ( $user_list as $user ) { ?>
    <tr>
        <td><?=$user[id]?></td>
        <td><?=$user[name]?></td>
    </tr>
    <? } ?>
</table>

</body>

</html>


test_htm.php文件对类库调用示例,本文件存放在 / 目录下
下面是对这样的html类型的模板出理调用的具体过程.

<?
require_once('includes/template/template.php');

$tpl = new Template;
$tpl->SetFile('templates/tpls/ver.1/test.php.php', TPL_PHP);

// 对于 php 文件来说, 此设置无效
//$tpl->SetCacheDir('cache/');
//

$tpl->SetVar('title','I love jear');
$tpl->SetVar('condition',TRUE);
$tpl->SetVar('a','a');
$tpl->SetVar('b','a');

$users_list = array(
    array(
        
'id' => '1',
        
'name' => 'Stangly.wrong'
    
),
    array(
        
'id' => '2',
        
'name' => 'Jear'
        
),
    );
$tpl->SetVar('user_list',$users_list);

echo
$tpl->Parse();
?>

以上就是模板类的调用示例,以及模板文件内容的格式。

最后给出template.php文件,他存放于 /includes/template/ 目录下。

<?
// -------------------------------------------------------
// Filename     : Template.inc.php
// Created      : 2004-11-11 13:46
// Author       : Stangly Wrong
// Version      : 2.1.4
// Description  : Parse template
// Modified     : 2004-11-29 14:12
// -------------------------------------------------------

define('TPL_PHP', 'php' );
define('TPL_HTM', 'htm' );
define('TPL_CACHE_DIR', './cache/');

class
TemplateEngine
{
    var
$_TplDir;
    var
$_File = NULL;
    var
$_Var = array();
    var
$_Err = FALSE;

    var
$_error_center;

    function
TemplateEngine()
    {
        if (!
class_exists('ErrorCenter') || !is_a($GLOBALS['error_center'], 'ErrorCenter'))
             die(
'Can not initialize ErrorCenter !');
         else
             
$this->_error_center = &$GLOBALS['error_center'];
         return;
    }

    function
_Parse($FileName = '' )
    {
        
$SourceFile = ( $FileName == '' ) ? $this->_TplDir.$this->_File : $FileName;
        if (!
file_exists($SourceFile ) )
            
$this->_error_center->AppendError('tpl_1', 'Template file was not found !');
        @
extract($this->_Var);
        
ob_start();
        include(
$SourceFile);
        
$contents = &ob_get_contents();
        
ob_end_clean();
        return
$contents;
    }

    function
_SetFile($FileName )
    {
        
$this->_File = basename($FileName );
        
$this->_TplDir = dirname($FileName).'/';
    }

    function
SetVar($name, $var = '')
    {
        if (
is_array($name ) )
            foreach (
$name as $k => $v )
                
$this->_Var[$k] = $v;
        else
            
$this->_Var[$name] = $var;
    }

    function
ClearVar()
    {
        unset(
$this->_Var );
    }
}

class
Template extends TemplateEngine
{
    var
$_CacheDir = TPL_CACHE_DIR;
    var
$_CompiledExt = '.php';
    var
$_CompilePre = '_c_co_';
    var
$_TplType = TPL_PHP;
    var
$_MultiCacheDir = TRUE;

    var
$_left_d = '/{';
    var
$_right_d = '/}';

    var
$_Compile_P = array(
        
0   => '//</?(.*)/?/>/',
        
1   => '//{([^/}/s/r/n/;]+)/}/',
        
2   => '//{/s*LIST ([a-zA-Z0-9_/[/]/']+)/s+AS/s+([a-zA-Z0-9_/[/]/']+)/s*/}/',
        
3   => '//{/s*ENDLIST/s*/}/',
        
4    => '//{/s*LIST/s+([a-zA-Z0-9_]+)/s+AS/s+([a-zA-Z0-9_/[/]/']+)/s+=>/s+([a-zA-Z0-9_/[/]/']+)/s*/}/',
        
5    => '//{/s*IF/s+([a-zA-Z0-9_/[/]/']+)/s*/}/',
        
6    => '//{/s*ENDIF/s*/}/',
        
7    => '//{/s*IF/s+([a-zA-Z0-9_/[/]/']+)/s+(==|!=|<|>|<=|>=)/s+([a-zA-Z0-9_/[/]/']+)/s*/}/',
        
8    => '//{/s*ELSE/s*/}/',
    );

    var
$_Compile_S = array(
        
0   => '<!-- Disabled PHP code -->',
        
1   => '<?php echo /$/1; ?>',
        
2   => '<?php foreach ( /$/1 as /$/2 ) { ?>',
        
3   => '<?php } ?>',
        
4    => '<?php foreach ( /$/1 as /$/2 => /$/3 ) { ?>',
        
5    => '<?php if ( /$/1 ) { ?>',
        
6    => '<?php } ?>',
        
7    => '<?php if ( /$/1 /2 /$/3 ) { ?>',
        
8    => '<?php } else { ?>',
    );

    function
Template()
    {
        
TemplateEngine::TemplateEngine();
    }

    function
_GenerateCompiledFileName($FileName = '' )
    {
        
$_return = ( $FileName == '' ) ? $this->_File : $FileName;
        if (
$this->_MultiCacheDir )
        {
            
$_temp = @preg_replace('/^(/.//|/.{2}//)/', '', $this->_TplDir);
            return
$this->_CacheDir.$_temp.$this->_CompilePre.$_return.$this->_CompiledExt;
        }
        else
             return
$this->_CacheDir.$this->_CompilePre.$_return.$this->_CompiledExt;
    }

    function
_MkDir($dir, $dirmode=700 )
    {
         
$dir = dirname($dir );
        if (!empty(
$dir))
        {
            if (!
file_exists($dir))
            {
                
preg_match_all('/([^//]*)//?/i', $dir,$atmp);
                
$base="";
                foreach (
$atmp[0] as $key=>$val)
                {
                    
$base=$base.$val;
                    if(!
file_exists($base))
                    if (!
mkdir($base,$dirmode))
                    {
                        echo
"Error: Cannot create ".$base;
                        return -
1;
                    }
                }
            }
            else
                if (!
is_dir($dir))
                {
                    echo
"Error: ".$dir." exists and is not a directory";
                    return -
2;
                }
        }
        return
0;
    }

    function
_Compile($FileName = '' )
    {
        
$SourceFile = ( $FileName == '' ) ? $this->_TplDir.$this->_File : $FileName;
        
$TargetFile = $this->_GenerateCompiledFileName($FileName);
        if (!
is_dir($this->_TplDir ) )
            
$this->_error_center->AppendError('tpl_3', 'Cache dir did not to establish !');
        if (!
file_exists($SourceFile ) )
            
$this->_error_center->AppendError('tpl_2', 'Cannot compile template file, because template file was not found !');
        
$fp = fopen($SourceFile, 'rb');
        
$content = @fread($fp, filesize($SourceFile ) );
        
fclose($fp );
        
$content = &preg_replace($this->_Compile_P, $this->_Compile_S, &$content);
        
$this->_MkDir($TargetFile, '0777');
        
$fp = fopen($TargetFile, 'wb' );
        
fwrite($fp, $content );
        
fclose($fp);
    }

    function
_IsExpired()
    {
        
$SourceFile = $this->_TplDir.$this->_File;
        
$TargetFile = $this->_GenerateCompiledFileName();
        
$SourceModTime = @filemtime($SourceFile);
        
$TargetModTime = @filemtime($TargetFile);
        if (
$SourceModTime < $TargetModTime )
            return
FALSE;
        else
            return
TRUE;
    }

    function
_ParseHtml()
    {
        if (
$this->_IsExpired() )
            
$this->_Compile();
        return @
$this->_Parse($this->_GenerateCompiledFileName($FileName) );
    }

    function
SetCacheDir($CacheDir )
    {
        
$this->_CacheDir = $CacheDir;
    }

    function
SetMultiCacheDir($s = TRUE )
    {
         
$this->_MultiCacheDir = $s;
    }

    function
SetFile($FileName, $TplType = TPL_PHP )
    {
        
$this->_TplType = $TplType;
        
$this->_SetFile($FileName);
    }

    function &
Parse()
    {
        if (
$this->_TplType == TPL_PHP )
            return
$this->_Parse();
        else
            return
$this->_ParseHtml();
    }
}
?>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值