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

来源:互联网 发布:在北京干什么挣钱知乎 编辑:程序博客网 时间:2024/05/17 05:17

        视图,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。


<?phprequire_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

0 0
原创粉丝点击