本文实例讲述了PHP实现简单的模板引擎功能。分享给大家供大家参考,具体如下:
php web开发中广泛采取mvc的设计模式,controller传递给view层的数据,必须通过模板引擎才能解析出来。实现一个简单的仅仅包含if,foreach标签,解析$foo变量的模板引擎。
编写template模板类和compiler编译类。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
<?php
namespace foo\base;
use foo\base\Object;
use foo\base\Compiler;
/**
*
*/
class Template extends Object
{
private $_config = [
'suffix' => '.php' , //文件后缀名
'templateDir' => '../views/' , //模板所在文件夹
'compileDir' => '../runtime/cache/views/' , //编译后存放的目录
'suffixCompile' => '.php' , //编译后文件后缀
'isReCacheHtml' => false, //是否需要重新编译成静态html文件
'isSupportPhp' => true, //是否支持php的语法
'cacheTime' => 0, //缓存时间,单位秒
];
private $_file ; //带编译模板文件
private $_valueMap = []; //键值对
private $_compiler ; //编译器
public function __construct( $compiler , $config = [])
{
$this ->_compiler = $compiler ;
$this ->_config = array_merge ( $this ->_config, $config );
}
/**
* [assign 存储控制器分配的键值]
* @param [type] $values [键值对集合]
* @return [type] [description]
*/
public function assign( $values )
{
if ( is_array ( $values )) {
$this ->_valueMap = $values ;
} else {
throw new \Exception( '控制器分配给视图的值必须为数组!' );
}
return $this ;
}
/**
* [show 展现视图]
* @param [type] $file [带编译缓存的文件]
* @return [type] [description]
*/
public function show( $file )
{
$this ->_file = $file ;
if (! is_file ( $this ->path())) {
throw new \Exception( '模板文件' . $file . '不存在!' );
}
$compileFile = $this ->_config[ 'compileDir' ] . md5( $file ) . $this ->_config[ 'suffixCompile' ];
$cacheFile = $this ->_config[ 'compileDir' ] . md5( $file ) . '.html' ;
//编译后文件不存在或者缓存时间已到期,重新编译,重新生成html静态缓存
if (! is_file ( $compileFile ) || $this ->isRecompile( $compileFile )) {
$this ->_compiler->compile( $this ->path(), $compileFile , $this ->_valueMap);
$this ->_config[ 'isReCacheHtml' ] = true;
if ( $this ->isSupportPhp()) {
extract( $this ->_valueMap, EXTR_OVERWRITE); //从数组中将变量导入到当前的符号表
}
}
if ( $this ->isReCacheHtml()) {
ob_start();
ob_clean();
include ( $compileFile );
file_put_contents ( $cacheFile , ob_get_contents());
ob_end_flush();
} else {
readfile( $cacheFile );
}
}
/**
* [isRecompile 根据缓存时间判断是否需要重新编译]
* @param [type] $compileFile [编译后的文件]
* @return boolean [description]
*/
private function isRecompile( $compileFile )
{
return time() - filemtime ( $compileFile ) > $this ->_config[ 'cacheTime' ];
}
/**
* [isReCacheHtml 是否需要重新缓存静态html文件]
* @return boolean [description]
*/
private function isReCacheHtml()
{
return $this ->_config[ 'isReCacheHtml' ];
}
/**
* [isSupportPhp 是否支持php语法]
* @return boolean [description]
*/
private function isSupportPhp()
{
return $this ->_config[ 'isSupportPhp' ];
}
/**
* [path 获得模板文件路径]
* @return [type] [description]
*/
private function path()
{
return $this ->_config[ 'templateDir' ] . $this ->_file . $this ->_config[ 'suffix' ];
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
<?php
namespace foo\base;
use foo\base\Object;
/**
*
*/
class Compiler extends Object
{
private $_content ;
private $_valueMap = [];
private $_patten = [
'#\{\\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}#' ,
'#\{if (.*?)\}#' ,
'#\{(else if|elseif) (.*?)\}#' ,
'#\{else\}#' ,
'#\{foreach \\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)}#' ,
'#\{\/(foreach|if)}#' ,
'#\{\\^(k|v)\}#' ,
];
private $_translation = [
"<?php echo \$this->_valueMap['\\1']; ?>" ,
'<?php if (\\1) {?>' ,
'<?php } else if (\\2) {?>' ,
'<?php }else {?>' ,
"<?php foreach (\$this->_valueMap['\\1'] as \$k => \$v) {?>" ,
'<?php }?>' ,
'<?php echo \$\\1?>'
];
/**
* [compile 编译模板文件]
* @param [type] $source [模板文件]
* @param [type] $destFile [编译后文件]
* @param [type] $values [键值对]
* @return [type] [description]
*/
public function compile( $source , $destFile , $values )
{
$this ->_content = file_get_contents ( $source );
$this ->_valueMap = $values ;
if ( strpos ( $this ->_content, '{$' ) !== false) {
$this ->_content = preg_replace( $this ->_patten, $this ->_translation, $this ->_content);
}
file_put_contents ( $destFile , $this ->_content);
}
}
|
我们的控制器就可以调用template中的assign方法进行赋值,show方法进行模板编译了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
/**
* [render 渲染模板文件]
* @param [type] $file [待编译的文件]
* @param [type] $values [键值对]
* @param array $templateConfig [编译配置]
* @return [type] [description]
*/
protected function render( $file , $values , $templateConfig = [])
{
$di = Container::getInstance();
//依赖注入实例化对象
$di ->template = function () use ( $di , $templateConfig ) {
$di ->compiler = 'foo\base\Compiler' ;
$compiler = $di ->compiler;
return new \foo\base\Template( $compiler , $templateConfig );
};
$di ->template->assign( $values )->show( $file );
}
|
Container类如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
<?php
namespace foo\base;
use foo\base\Object;
class Container extends Object
{
private static $_instance ;
private $s = [];
public static $instances = [];
public static function getInstance()
{
if (!(self:: $_instance instanceof self)) {
self:: $_instance = new self();
}
return self:: $_instance ;
}
private function __construct(){}
private function __clone(){}
public function __set( $k , $c )
{
$this ->s[ $k ] = $c ;
}
public function __get( $k )
{
return $this ->build( $this ->s[ $k ]);
}
/**
* 自动绑定(Autowiring)自动解析(Automatic Resolution)
*
* @param string $className
* @return object
* @throws Exception
*/
public function build( $className )
{
// 如果是闭包函数(closures)
if ( $className instanceof \Closure) {
// 执行闭包函数
return $className ( $this );
}
if (isset(self:: $instances [ $className ])) {
return self:: $instances [ $className ];
}
/** @var ReflectionClass $reflector */
$reflector = new \ReflectionClass( $className );
// 检查类是否可实例化, 排除抽象类abstract和对象接口interface
if (! $reflector ->isInstantiable()) {
throw new \Exception( $reflector . ': 不能实例化该类!' );
}
/** @var ReflectionMethod $constructor 获取类的构造函数 */
$constructor = $reflector ->getConstructor();
// 若无构造函数,直接实例化并返回
if ( is_null ( $constructor )) {
return new $className ;
}
// 取构造函数参数,通过 ReflectionParameter 数组返回参数列表
$parameters = $constructor ->getParameters();
// 递归解析构造函数的参数
$dependencies = $this ->getDependencies( $parameters );
// 创建一个类的新实例,给出的参数将传递到类的构造函数。
$obj = $reflector ->newInstanceArgs( $dependencies );
self:: $instances [ $className ] = $obj ;
return $obj ;
}
/**
* @param array $parameters
* @return array
* @throws Exception
*/
public function getDependencies( $parameters )
{
$dependencies = [];
/** @var ReflectionParameter $parameter */
foreach ( $parameters as $parameter ) {
/** @var ReflectionClass $dependency */
$dependency = $parameter ->getClass();
if ( is_null ( $dependency )) {
// 是变量,有默认值则设置默认值
$dependencies [] = $this ->resolveNonClass( $parameter );
} else {
// 是一个类,递归解析
$dependencies [] = $this ->build( $dependency ->name);
}
}
return $dependencies ;
}
/**
* @param ReflectionParameter $parameter
* @return mixed
* @throws Exception
*/
public function resolveNonClass( $parameter )
{
// 有默认值则返回默认值
if ( $parameter ->isDefaultValueAvailable()) {
return $parameter ->getDefaultValue();
}
throw new \Exception( 'I have no idea what to do here.' );
}
}
|
要想以键值对的方式访问对象的属性必须实现ArrayAccess接口的四个方法,
Object基类代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
public function offsetExists( $offset )
{
return array_key_exists ( $offset , get_object_vars( $this ));
}
public function offsetUnset( $key )
{
if ( array_key_exists ( $key , get_object_vars( $this )) ) {
unset( $this ->{ $key });
}
}
public function offsetSet( $offset , $value )
{
$this ->{ $offset } = $value ;
}
public function offsetGet( $var )
{
return $this -> $var ;
}
|
在某一控制器中就可以调用父类Controller的render方法啦
复制代码 代码如下:
$this->render('test\index', ['name' => 'tom', 'age' => 20, 'friends' => ['jack', 'rose']], ['cacheTime' => 10]);
编写视图模板文件'test\index':
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< title >Document</ title >
</ head >
< body >
< p >展示模板文件视图</ p >
< p >{$name}</ p >
< p >{$age}</ p >
<? php echo ++$age;?>
{if $age > 18}
< p >已成年</ p >
{else if $age < 10 }
<p>小毛孩</ p >
{/if}
{foreach $friends}
< p >{^v} </ p >
{/foreach}
</ body >
</ html >
|
至此,一个简单的模板编译引擎就写好了。
希望本文所述对大家PHP程序设计有所帮助。
原文链接:http://blog.csdn.net/u012371137/article/details/51996675