本文实例讲述了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