本文实例讲述了PHP实现简单的模板引擎功能。分享给大家供大家参考,具体如下:

php web开发中广泛采取mvc的设计模式,controller传递给view层的数据,必须通过模板引擎才能解析出来。实现一个简单的仅仅包含if,foreach标签,解析$foo变量的模板引擎。

编写template模板类和compiler编译类。代码如下:

<?phpnamespace fooase;use fooaseObject;use fooaseCompiler;/*** */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"]; }}
<?phpnamespace fooase;use fooaseObject;/*** */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方法进行模板编译了。

/*** [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 = "fooaseCompiler";   $compiler = $di->compiler;   return new fooaseTemplate($compiler, $templateConfig);  };  $di->template->assign($values)->show($file);}

Container类如下:

<?phpnamespace fooase;use fooaseObject;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基类代码如下:

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("testindex", ["name" => "tom", "age" => 20, "friends" => ["jack", "rose"]], ["cacheTime" => 10]);

编写视图模板文件"testindex":

<!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相关内容感兴趣的读者可查看本站专题:《PHP模板技术总结》、《PHP基于pdo操作数据库技巧总结》、《PHP运算与运算符用法总结》、《PHP网络编程技巧总结》、《PHP基本语法入门教程》、《php面向对象程序设计入门教程》、《php字符串(string)用法总结》、《php+mysql数据库操作入门教程》及《php常见数据库操作技巧汇总》

希望本文所述对大家PHP程序设计有所帮助。