zoukankan      html  css  js  c++  java
  • Thinkphp源码分析系列(九)–视图view类

    视图类view主要用于页面内容的输出,模板调用等,用在控制器类中,可以使得控制器类把表现和数据结合起来。下面我们来看一下执行流程。

    首先,在控制器类中保持着一个view类的对象实例,只要继承自控制器父类的类都可以使用这个实例,所以我们在控制器子类中就可以使用view类实例去很容易的调用模板,输出内容。

    看Controller.class.php类的第22行和35行,分别声明了一个实例变量,在构造函数中实例化一个view实例。

    // +----------------------------------------------------------------------
    // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
    // +----------------------------------------------------------------------
    // | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
    // +----------------------------------------------------------------------
    // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
    // +----------------------------------------------------------------------
    // | Author: liu21st <liu21st@gmail.com>
    // +----------------------------------------------------------------------
    namespace Think;
    /**
     * ThinkPHP 控制器基类 抽象类
     */
    abstract class Controller {
    
        /**
         * 视图实例对象
         * @var view
         * @access protected
         */    
        protected $view     =  null;
    
        /**
         * 控制器参数
         * @var config
         * @access protected
         */      
        protected $config   =   array();
    
       /**
         * 架构函数 取得模板对象实例
         * @access public
         */
        public function __construct() {
            Hook::listen('action_begin',$this->config);
            //实例化视图类
            $this->view     = Think::instance('ThinkView');
            //控制器初始化
            if(method_exists($this,'_initialize'))
                $this->_initialize();
    }

    我们在控制器类中输出模板和内容主要调用控制器类中的display方法,但是我们查看此方法就会发现,此方法里面调用的还是view类的display方法,display的主要实现逻辑还在view类中。在view类的大概67行左后,我们就可以看到这个函数的实现过程,下面来分析一个display方法。

    /**
    * 加载模板和页面输出 可以返回输出内容
    * @access public
    * @param string $templateFile 模板文件名
    * @param string $charset 模板输出字符集
    * @param string $contentType 输出类型
    * @param string $content 模板输出内容
    * @param string $prefix 模板缓存前缀
    * @return mixed
    */
    public function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') {
    G('viewStartTime');
    // 视图开始标签
    Hook::listen('view_begin',$templateFile);
    // 解析并获取模板内容
    $content = $this->fetch($templateFile,$content,$prefix);
    // 输出模板内容
    $this->render($content,$charset,$contentType);
    // 视图结束标签
    Hook::listen('view_end');
    }

    我们可以看到在display方法中也是分别调用多个函数来协作完成模板处理工作的。

    我们来整理分析一下流程。

    dispaly方法接受一个templateFile参数,调用parseTemplate方法根据这个参数去侦测模板文件的位置,结合主题组合出一个模板的地址,执行view_parse标签行为,在行为类里面去调用模板引擎的fetch方法去解析模板,返回编译后的内容。

    所以我们知道,对于视图类来说,大概分为以下几步,第一是根据参数去侦测出模板文件的地址,然后调用模板引擎去编译模板文件,形成编译缓存php文件,然后执行php文件返回内容。

    下面我们来分析一下tp是怎样根据用户传进来的参数来侦测模板文件的地址的。核心代码逻辑在view类的140行左右。

    /**
    * 自动定位模板文件
    * @access protected
    * @param string $template 模板文件规则
    * @return string
    */
    public function parseTemplate($template='') {
    //如果$template是一个文件地址的话,那么就直接返回该地址。这是最简单的一种使用方式。
    //这里的文件地址是一个相对文件路径地址,要注意模板文件位置是相对于项目的入口文件
    if(is_file($template)) {
    return $template;
    }
    //获取模板文件的分隔符,模板文件CONTROLLER_NAME与ACTION_NAME之间的分割符
    $depr = C('TMPL_FILE_DEPR');
    //将冒号替换成分隔符
    $template = str_replace(':', $depr, $template);
    // 获取当前主题名称
    $theme = $this->getTemplateTheme();
    /*
    我们来看一下tp的模板侦测逻辑。
    首先tp要求我们传入一个地址表达式。格式如下:
    [模块@][控制器:][操作]  比如:  m@c:a  表示m模块下的c控制器下的a方法
    我们只要给此方法传入模块,控制器和方法三个参数,这个方法就能给我们侦测出对应的模板文件地址。
    具体来说,他会先定义出截止到模块这个阶段的目录,然后在得到控制器到最后的模板文件地址,最后组合起来形成最终的文件地址。
    1:先定义模块的目录
    首先解析地址表达式,如果有@,表示传入了模块地址,解析她,赋值给module变量。
    我们的控制器文件夹和方法存放在哪里呢?
    如果定义了视图目录,就存放在视图目录中,如果没有定义,就看看是否定义了模板路径,如果定义了就存放在该路径下的对应模块目录下,如果没有定义模板路径,默认就存放在应用文件夹下的对应模块文件夹下的默认视图层下。
    最后我们会得到一个THEME_PATH表示控制器模板存放的目录。如果有主题的话加上主题
    
    
    */
    // 获取当前模块
    $module = MODULE_NAME;
    if(strpos($template,'@')){ // 跨模块调用模版文件
    list($module,$template) = explode('@',$template);
    }
    // 获取当前主题的模版路径
    if(!defined('THEME_PATH')){
    if(C('VIEW_PATH')){ // 模块设置独立的视图目录
    $tmplPath = C('VIEW_PATH');
    }else{ // 定义TMPL_PATH 改变全局的视图目录到模块之外
    $tmplPath = defined('TMPL_PATH')? TMPL_PATH.$module.'/' : APP_PATH.$module.'/'.C('DEFAULT_V_LAYER').'/';
    }
    define('THEME_PATH', $tmplPath.$theme);
    }
    
    // 分析模板文件规则
    /*
    然后分析地址表达式中的模板规则,如果地址表达式为空,或者只是传了一个模块名,那么这里的template就为空,那么默认的地址就是默认控制器下的默认方法
    */
    if('' == $template) {
    // 如果模板文件名为空 按照默认规则定位
    $template = CONTROLLER_NAME . $depr . ACTION_NAME;
    }elseif(false === strpos($template, $depr)){
    $template = CONTROLLER_NAME . $depr . $template;
    }
    //最后结合一下得到最后的模板文件地址
    $file = THEME_PATH.$template.C('TMPL_TEMPLATE_SUFFIX');
    if(C('TMPL_LOAD_DEFAULTTHEME') && THEME_NAME != C('DEFAULT_THEME') && !is_file($file)){
    // 找不到当前主题模板的时候定位默认主题中的模板
    $file = dirname(THEME_PATH).'/'.C('DEFAULT_THEME').'/'.$template.C('TMPL_TEMPLATE_SUFFIX');
    }
    return $file;
    }

    到这里为止,我们已经根据用户传入的模板地址表达式得到了模板文件地址,接下来就是调用模板引擎来解析这个模板。在view类的fetch方法中并没有直接调用模板引擎template类的的方法去解析模板,而是去调用了一个view_parse标签,在这个标签上绑定了行为模式扩展类ParseTemplateBehavior,模板的解析就是在这个类的run方法中进行的,这个类总我们不仅而已使用tp自带的模板引擎,还可以使用其他开源第三方的模板引擎类,具有很好的高扩展性。关于模板引擎解析模板的裸机,请看源码分析8

  • 相关阅读:
    Java Lock Example – ReentrantLock(java锁的例子)
    Java Scheduler ScheduledExecutorService ScheduledThreadPoolExecutor Example(ScheduledThreadPoolExecutor例子——了解如何创建一个周期任务)
    ThreadPoolExecutor – Java Thread Pool Example(java线程池创建和使用)
    Java Timer TimerTask Example(java Timer的例子)
    Java ThreadLocal Example(java中的ThreadLocal例子)
    Thread Safety in Java(java中的线程安全)
    java 线程方法join的简单总结
    JAVA多线程和并发基础
    BAT面试常的问题和最佳答案
    linux服务脚本编写
  • 原文地址:https://www.cnblogs.com/ronghua/p/6006766.html
Copyright © 2011-2022 走看看