php實現模板引擎功能的簡單樣本

來源:互聯網
上載者:User
本文執行個體講述了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>

至此,一個簡單的模板編譯引擎就寫好了。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.