PHP搭建自己的web框架-视图/模板引擎

        视图,MVC中的V,View,如何将数据通过合适的格式展现给用户或调用方。

        当然使用什么格式展现由控制器直接控制,但根本原因由人或系统决定。


        本文主要描述的是如何在MVC的web框架中输出网页视图,也就是HTML格式的视图。


        从开始学习PHP的教程,一般都是直接在PHP中嵌入HTML,这种方式简单粗暴,但也是最根本最高效的方式。

        回想一下,上面部分代码是逻辑和数据准备,下面部分是嵌入HTML的PHP代码。

        这里不能容忍的不是PHP中嵌入HTML,而是业务逻辑与视图不分离,从代码分离、人员分工等角度来说都不是很好的做法。

        MVC的主要思想是层次分明,功能逻辑与视图分离,而是否在视图中使用PHP原生语法,站在MVC角度并不是主要关心的。所以作为MVC中视图的模板引擎,第一要素是分离,其次才是考虑性能与语法。

        对PHP有是否使用模板引擎之争,以分离为核心的MVC,并不一定要使用模板引擎,但使用模板引擎能更好地分离,分离得更清晰。

如果以过程式开发,可以在A文件写逻辑,然后最后require B,B写视图文件。这种情况,不强调模板引擎。

而对于面向对象的开发,上述方式显得异样;或者说受java或ruby on rails影响,会抽象出专门的模板引擎来渲染视图。这种情况讲模板引擎才更有意义。


对于一些开源项目或框架的做法,我遇到过的这里简单列举一下。

1.  跟入门教程一样,业务逻辑与视图显示部分混在一起。

2. 把页面元素封装成一个个构件方法。这种方式不把流程看下来,都不知道哪输出了视图,而且布局、定位方面不太容易理解。

3. 通常意义下的视图做法,组合一个模板引擎,初始化模板引擎,assign数据,display显示视图。比如:

$this->view = new View();
$this->view->tpl_dir = PATH_PAGE_VIEW;
$this->view->cache_dir = PATH_PAGE_CACHE;
$this->view->cache_time = TPL_CACHE_TIME;
$view->assign("name","lory");
$view->assign("uid",123456);
$view->display("index.tpl");// or $view->renderHtml('index.tpl);

4. 因为每次assign显示很麻烦,有些做法是把整个控制器对象传给视图,由视图取出public属性。

这种方式挺好,推荐使用。


接下来是两个主题,怎么实现一个模板引擎和怎样使用模板引擎。

如何实现模板引擎

说到模板引擎,smarty对PHP来说是绕不开的,各种方法、功能和机制完备,其中上所有场景有考虑到吧,但实际上在项目中使用时,只使用了其很少的几个特性和功能,同时其性能也不好。因此选择其它模板引擎或自己实现一个,也是有必要的,关键是不难。

看一下smarty的实现,或搜索一下PHP模板引擎,很多相关实现,本文也不详细说明。可以看看tmd_tpl,我们项目就是在tmd_tpl基础上修改了一下。

可以看出,模板引擎编译出来的缓存文件,都是PHP中嵌入HTML的方式,模板引擎只是做了转换的角色,最终执行的是缓存出来的缓存文件。

如果模板引擎自定义了语法,都是将自定义语法通过匹配替换的方式转化为PHP语法的过程,虽然编译过程效率不高,但如果直接加载缓存结果,也不会有多大损耗。

在自己实现时,页面如何接收数据,可以关注方法extract,或是将页面变量替换成一个方法来接收值。


如何使用模板引擎

一般模板引擎都有使用说明,下面是我们项目中的做法,仅为参考。

将模板引擎组合到控制器父类中,而每一个业务控制器作为子类,即可使用模板引擎。

一般情况下,需要对模板引擎做些封装,子类不会直接调用到模板引擎的方法,而是通过父类方法间接调用。

在tmd_tpl基础上,增加了方法:

function assignObj($obj) {
$data = get_object_vars ( $obj );
foreach ( $data as $key => $value ) {
$this->Assign ( $key, $value );
}
}

这样只要是类的public属性,即可传递给模板,而不用一个一个assign。


<?php
require_once PATH_LIB . 'tmd_tpl.php';

/**
 */
class PCAction {
	protected $view;

	public $page_title = '模板引擎示例';
	
	/**
	 * 网站header/footer部分
	 */
	public $header_tpl = 'common/header.html';
	public $footer_tpl = 'common/footer.html';
	
	/**
	 * 网站内容部分
	 */
	public $page_tpl;
	
	/**
	 * $page_frame里包含了header_tpl/footer_tpl/page_tpl的布局
	 */
	public $page_frame = 'common/main_frame.html';
	public $more_css = '';
	public $more_js = '';
	public $more_footer_js;
	
	function __construct() {
	}
	
	/**
	 * 设置模板必要的参数。
	 */
	function initViewConfig() {
		$this->view = new tmd_tpl ();
		$this->view->tpl_dir = PATH_PAGE_VIEW;
		$this->view->cache_dir = PATH_PAGE_CACHE;
		$this->view->cache_time = TPL_CACHE_TIME;
		$this->view->my_rep = array (
				'~__ROOT__~' => URL_HOST,
				'~__JSPATH__~' => URL_JS,
				'~__CSSPATH__~' => URL_CSS,
				'~__PLUGINPATH__~' => URL_PLUGIN,
				'~__IMAGEPATH__~' => URL_IMAGE,
				'~__VERSION__~' => UPDATE_TIME 
		);
	}
	
	/**
	 * PC端WEB页面的显示。
	 *
	 * @param string $tpl
	 *        	如果指定了,只显示tpl模板,没有指定则显示整个框架模板。
	 */
	protected function display($tpl = '') {
		$this->initViewConfig ();
		
		$this->view->assignobj ( $this );
		
		if ($tpl) {
			$this->view->display ( $tpl );
		} else {
			if (! $this->page_tpl) {
				$this->page_tpl = 'index.html';
			}
			$this->view->display ( $this->page_frame );
		}
		exit ();
	}

	protected function addMoreCss($css_path) {
		$this->more_css .= '<link href="' . URL_CSS . $css_path . '?v=' . UPDATE_TIME . '" rel="stylesheet" type="text/css"/>';
	}
	protected function addMoreJs($js_path) {
		$this->more_js .= '<script type="text/javascript" src="' . URL_JS . $js_path . '?v=' . UPDATE_TIME . '"?></script>';
	}
	protected function addMoreFooterJs($js_path) {
		$this->more_footer_js .= '<script type="text/javascript" src="' . URL_JS . $js_path . '?v=' . UPDATE_TIME . '"></script>';
	}
}


main_frame.html:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{$page_title}</title>
        <link href="__CSSPATH__common.css?v=<?php echo UPDATE_TIME;?>" media="all" rel="stylesheet" type="text/css">
		{$more_css}
		{$more_js}
    </head>
    <body atk="{$ajax_token}">
        <?php include ('common/header.html');?>
        <div class="g-bd">
            <?php if($page_tpl){ ?>
            <?php include ($page_tpl);?>
            <?php }?>
        </div>
        <?php include ('common/footer.html');?>
	{$more_footer_js}
    </body>
</html>

控制器使用时:

<?php 

class main extends PCAction{
	
	public function index(){
		$this->name = 'frogluo';
		$this->age = 32;
		$this->uid = 123456;
		$this->is_admin = 0;
		
		$this->page_tpl = 'index.tpl';
		$this->display();
		//或
		//$this->display('index.tpl');
	}
}


对tmd_tpl变量使用自定义语法,采取替换,流程控制使用原生PHP语法,我觉得是比较好的一种实践组合。

tmd_tpl把模板引擎的基本思想都体现出来了,因此学习与改造都比较适合。

同时还改造了将模板中require/include引用其它模板的内容合并到主模板中,具体可见:

https://github.com/frogluo/php/tmd_tpl

相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页