首页 > 编程 > PHP > 正文

PHP实现简单的模板引擎功能示例

2019-11-21 20:48:52
字体:
来源:转载
供稿:网友

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

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

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

<?phpnamespace 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']; }}
<?phpnamespace 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方法进行模板编译了。

/*** [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类如下:

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

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':

<!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程序设计有所帮助。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表