ThinkPHP3.2.3源码分析一之系统流程

来源:互联网 发布:红黑树算法有什么用 编辑:程序博客网 时间:2024/05/06 09:54

ThinkPHP3.2.3源码分析一之系统流程

  • ThinkPHP323源码分析一之系统流程
    • 整体流程
    • 一 初始化
      • 主要文件加载
      • 简单流程
        • indexphp
        • ThinkPHPThinkPHPphp
        • ThinkPHPLibraryThinkThinkclassphp
        • ThinkPHPLibraryThinkStorageclassphp
        • ThinkPHPLibraryThinkStorageDriverFileclassphp or Sae
    • 二 应用模式的编译缓存和配置文件加载
      • 主要文件加载
      • 简单流程
        • ThinkPHPLibraryThinkThinkclassphp
    • 三 App初始化和URL解析定位模块控制器和操作
      • 主要文件加载
      • 简单流程
        • ThinkPHPLibraryThinkThinkclassphp
        • ThinkPHPLibraryThinkAppclassphp
    • 四 Action执行和模板解析
      • 主要文件加载
      • 简单流程
        • ApplicationHomeControllerIndexControllerclassphp
        • thinkphp_323ThinkPHPLibraryThinkControllerclassphp
        • ThinkPHPLibraryThinkViewclassphp
        • ThinkPHPLibraryBehaviorParseTemplateBehaviorclassphp
    • 五 收尾流程
      • 简单流程
        • ThinkPHPLibraryThinkAppclassphp
        • ThinkPHPLibraryThinkThinkclassphp
  • END

整体流程

红色的时钩子,蓝色为配置文件或函数定义文件

这里写图片描述

一 初始化

主要文件加载

\thinkphp_3.2.3\index.php
\thinkphp_3.2.3\ThinkPHP\ThinkPHP.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\Think.class.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\Storage.class.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\Storage\Driver\File.class.php

简单流程

\index.php

// 检测PHP环境if(version_compare(PHP_VERSION,'5.3.0','<'))  die('require PHP > 5.3.0 !');// 开启调试模式 建议开发阶段开启 部署阶段注释或者设为falseWdefine('APP_DEBUG',True);// 定义应用目录define('APP_PATH','./Application/');// 引入ThinkPHP入口文件require './ThinkPHP/ThinkPHP.php';

\ThinkPHP\ThinkPHP.php

// 记录开始运行时间$GLOBALS['_beginTime'] = microtime(TRUE);// 记录内存初始使用define('MEMORY_LIMIT_ON',function_exists('memory_get_usage'));if(MEMORY_LIMIT_ON) $GLOBALS['_startUseMems'] = memory_get_usage();// 系统常量定义defined('THINK_PATH')   or define('THINK_PATH',     __DIR__.'/');defined('APP_PATH')     or define('APP_PATH',       dirname($_SERVER['SCRIPT_FILENAME']).'/');defined('APP_STATUS')   or define('APP_STATUS',     ''); // 应用状态 加载对应的配置文件defined('APP_DEBUG')    or define('APP_DEBUG',      false); // 是否调试模式///  用宏定义define定义大量的系统状态和系统路径/// 判断操作系统,php版本,运行方式(cgi,sapi)来特殊处理常量定义// 加载核心Think类require CORE_PATH.'Think'.EXT;// 应用初始化 Think\Think::start();

\ThinkPHP\Library\Think\Think.class.php

static public function start() {      // 注册AUTOLOAD方法      // 主要时去系统目录找类文件,或从应用配置文件注册的命名空间路径找      // array('Think','Org','Behavior','Com','Vendor')      // C('AUTOLOAD_NAMESPACE');      // 兼容处理不开命名空间的项目      spl_autoload_register('Think\Think::autoload');      // 设定系统错误和异常处理函数注册      // 脚本正常结束或者手动或异常终结处理      // 处理: 1.内存中的日志落地到文件 2.如果是异常终止,则输出错误      register_shutdown_function('Think\Think::fatalError');      //系统致命错误处理      //级别_USER_ERROR的写日志,页面输出提示      //低级别WARN NOTICE 等输出PageTrace,不影响脚本      set_error_handler('Think\Think::appError');      //系统抛出异常处理      //写日志,页面输出      set_exception_handler('Think\Think::appException');      // 初始化文件存储方式      Storage::connect(STORAGE_TYPE);

\ThinkPHP\Library\Think\Storage.class.php

\ThinkPHP\Library\Think\Storage\Driver\File.class.php (or Sae)

//此处文件缓存类,兼容本地缓存和sae的分布式文件环境。class Storage {    /**     * 操作句柄     * @var string     * @access protected     */    static protected $handler    ;    /**     * 连接分布式文件系统     * @access public     * @param string $type 文件类型     * @param array $options  配置数组     * @return void     */    static public function connect($type='File',$options=array()) {        $class  =   'Think\\Storage\\Driver\\'.ucwords($type);        self::$handler = new $class($options);    }    static public function __callstatic($method,$args){        ///////// 魔术方法 调用文件缓存驱动类的方法        //调用缓存驱动的方法        if(method_exists(self::$handler, $method)){           return call_user_func_array(array(self::$handler,$method), $args);        }    }}

二 应用模式的编译缓存和配置文件加载

主要文件加载

\thinkphp_3.2.3\ThinkPHP\Mode\common.php
\thinkphp_3.2.3\ThinkPHP\Common\functions.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\Hook.class.php

简单流程

\ThinkPHP\Library\Think\Think.class.php

$runtimefile  = RUNTIME_PATH.APP_MODE.'~runtime.php';        // 部署模式并且已缓存的情况下,直接load编译缓存文件,此处的load实质是include文件      if(!APP_DEBUG && Storage::has($runtimefile)){          Storage::load($runtimefile);      }else{          // debug模式或无缓存文件时          // 1.清理可能存在的过期缓存文件          // 2. 加载一堆配置文件,语言包(这里的配置加载到内寸中,一般是存在全局函数作用域的静态变量,或者在类的静态成员变量中)          // 3. 部署模式下,缓存起应用编译文件(编译过程,去掉注释空白,替换预编译的字符串, 写入导入配置的代码段)          // 4 debug模式下,导入debug对应的系统配置,应用配置          if(Storage::has($runtimefile))              Storage::unlink($runtimefile);          $content =  '';          // 读取应用模式          $mode   =   include is_file(CONF_PATH.'core.php')?CONF_PATH.'core.php':MODE_PATH.APP_MODE.'.php';          // 加载核心文件          foreach ($mode['core'] as $file){              if(is_file($file)) {                include $file;                if(!APP_DEBUG) $content   .= compile($file);              }          }          // 加载应用模式配置文件          foreach ($mode['config'] as $key=>$file){              is_numeric($key)?C(load_config($file)):C($key,load_config($file));          }          // 读取当前应用模式对应的配置文件          if('common' != APP_MODE && is_file(CONF_PATH.'config_'.APP_MODE.CONF_EXT))              C(load_config(CONF_PATH.'config_'.APP_MODE.CONF_EXT));            // 加载模式别名定义          if(isset($mode['alias'])){              self::addMap(is_array($mode['alias'])?$mode['alias']:include $mode['alias']);          }          // 加载应用别名定义文件          if(is_file(CONF_PATH.'alias.php'))              self::addMap(include CONF_PATH.'alias.php');          // 加载模式行为定义          if(isset($mode['tags'])) {              Hook::import(is_array($mode['tags'])?$mode['tags']:include $mode['tags']);          }          // 加载应用行为定义          if(is_file(CONF_PATH.'tags.php'))              // 允许应用增加开发模式配置定义              Hook::import(include CONF_PATH.'tags.php');             // 加载框架底层语言包          L(include THINK_PATH.'Lang/'.strtolower(C('DEFAULT_LANG')).'.php');          if(!APP_DEBUG){              $content  .=  "\nnamespace { Think\\Think::addMap(".var_export(self::$_map,true).");";              $content  .=  "\nL(".var_export(L(),true).");\nC(".var_export(C(),true).');Think\Hook::import('.var_export(Hook::get(),true).');}';              Storage::put($runtimefile,strip_whitespace('<?php '.$content));          }else{            // 调试模式加载系统默认的配置文件            C(include THINK_PATH.'Conf/debug.php');            // 读取应用调试配置文件            if(is_file(CONF_PATH.'debug'.CONF_EXT))                C(include CONF_PATH.'debug'.CONF_EXT);                     }      }      // 读取当前应用状态对应的配置文件      if(APP_STATUS && is_file(CONF_PATH.APP_STATUS.CONF_EXT))          C(include CONF_PATH.APP_STATUS.CONF_EXT);         // 设置系统时区      date_default_timezone_set(C('DEFAULT_TIMEZONE'));      // 检查应用目录结构 如果不存在则自动创建          //////  生成模块目录和Runtime目录      if(C('CHECK_APP_DIR')) {          $module     =   defined('BIND_MODULE') ? BIND_MODULE : C('DEFAULT_MODULE');          if(!is_dir(APP_PATH.$module) || !is_dir(LOG_PATH)){              // 检测应用目录结构              Build::checkDir($module);          }      }      // 记录加载文件时间      G('loadTime');

三 App初始化和URL解析,定位模块控制器和操作

主要文件加载

\thinkphp_3.2.3\ThinkPHP\Library\Think\App.class.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\Dispatcher.class.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\Route.class.php

简单流程

\ThinkPHP\Library\Think\Think.class.php

 // 运行应用      App::run();

\ThinkPHP\Library\Think\App.class.php

 /**     * 运行应用实例 入口文件使用的快捷方法     * @access public     * @return void     */    static public function run()    {        // 应用初始化标签        /**                'app_init'     =>  array(                    'Behavior\BuildLiteBehavior', // 生成运行Lite文件                ),         *      生成lite文件,用于替换入口文件         */        Hook::listen('app_init');        // app初始化,见init()        App::init();        // 应用开始标签        /**         *           'app_begin'     =>  array(                             'Behavior\ReadHtmlCacheBehavior', // 读取静态缓存                      ),         *           若打开静态缓存开关,则直接获取缓存文件,输出,终止脚本。         */        Hook::listen('app_begin');        // Session初始化        if (!IS_CLI) {            session(C('SESSION_OPTIONS'));        }        // 记录应用初始化时间        G('initTime');        /**app执行 :定位到controller action,并实例化执行之         *         *  //创建控制器实例              $module = controller(CONTROLLER_NAME, CONTROLLER_PATH);         *   $action = ACTION_NAME . C('ACTION_SUFFIX');         *         * //反射类,此处不展开说明         *    $method = new \ReflectionMethod($module, $action);         *    $class = new \ReflectionClass($module);         *         *    self::invokeAction($module, $action);         *   用到反射类API,作用:         *     判断方法公共,静态等         *    前置方法,后置方法,空方法捕获并调用         *     实现控制器方法的参数绑定         *     调用action方法及上述方法         */        App::exec();        ///  ...        }/**     * 应用程序初始化     * @access public     * @return void     */    static public function init()    {        // 加载动态应用公共文件和配置        // 加载应用配置文件下非系统定义的配置文件名,   C('LOAD_EXT_FILE')) C('LOAD_EXT_CONFIG'))        load_ext_file(COMMON_PATH);        // 日志目录转换为绝对路径 默认情况下存储到公共模块下面        C('LOG_PATH', realpath(LOG_PATH) . '/Common/');        // 定义当前请求的系统常量        define('NOW_TIME', $_SERVER['REQUEST_TIME']);        define('REQUEST_METHOD', $_SERVER['REQUEST_METHOD']);        define('IS_GET', REQUEST_METHOD == 'GET' ? true : false);        define('IS_POST', REQUEST_METHOD == 'POST' ? true : false);        define('IS_PUT', REQUEST_METHOD == 'PUT' ? true : false);        define('IS_DELETE', REQUEST_METHOD == 'DELETE' ? true : false);        // URL调度        // 主要是根据url模式,路由配置,子域名配置等,确认下面3个常量        // 此处不展开说明        /**         * define('MODULE_NAME', defined('BIND_MODULE')? BIND_MODULE : self::getModule($varModule));               *  define('CONTROLLER_NAME',   defined('BIND_CONTROLLER')? BIND_CONTROLLER : self::getController($varController,$urlCase));            define('ACTION_NAME',       defined('BIND_ACTION')? BIND_ACTION : self::getAction($varAction,$urlCase));         */        Dispatcher::dispatch();        if (C('REQUEST_VARS_FILTER')) {            // 全局安全过滤            array_walk_recursive($_GET, 'think_filter');            array_walk_recursive($_POST, 'think_filter');            array_walk_recursive($_REQUEST, 'think_filter');        }        // URL调度结束标签,官方无动作        Hook::listen('url_dispatch');        define('IS_AJAX', ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') || !empty($_POST[C('VAR_AJAX_SUBMIT')]) || !empty($_GET[C('VAR_AJAX_SUBMIT')])) ? true : false);        // TMPL_EXCEPTION_FILE 改为绝对地址        C('TMPL_EXCEPTION_FILE', realpath(C('TMPL_EXCEPTION_FILE')));        return;    }

四 Action执行和模板解析

主要文件加载

\thinkphp_3.2.3\ThinkPHP\Library\Think\Controller.class.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\View.class.php
\thinkphp_3.2.3\Application\Home\Controller\IndexController.class.php
\thinkphp_3.2.3\ThinkPHP\Library\Behavior\ParseTemplateBehavior.class.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\Template.class.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\Template\TagLib\Cx.class.php
\thinkphp_3.2.3\ThinkPHP\Library\Think\Template\TagLib.class.php

简单流程

\Application\Home\Controller\IndexController.class.php

namespace Home\Controller;use Think\Controller;class IndexController extends Controller {    public function index(){        $this->display();    }}

\thinkphp_3.2.3\ThinkPHP\Library\Think\Controller.class.php

namespace Think;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);        //实例化视图类        // Controller类方法display assign fetch show 等都是调用此实例的对应方法        $this->view     = Think::instance('Think\View');        //控制器初始化        if(method_exists($this,'_initialize'))            $this->_initialize();    }/**     * 模板显示 调用内置的模板引擎显示方法,     * @access protected     * @param string $templateFile 指定要调用的模板文件     * 默认为空 由系统自动定位模板文件     * @param string $charset 输出编码     * @param string $contentType 输出类型     * @param string $content 输出内容     * @param string $prefix 模板缓存前缀     * @return void     */    protected function display($templateFile='',$charset='',$contentType='',$content='',$prefix='') {        $this->view->display($templateFile,$charset,$contentType,$content,$prefix);    } // 此外此类还用魔术方法实现空操作的跳转和默认模板的调用// 也实现了页面的重定向和自动跳转

\ThinkPHP\Library\Think\View.class.php

/**     * 加载模板和页面输出 可以返回输出内容     * @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);        // 解析并获取模板内容 见fetch()        $content = $this->fetch($templateFile,$content,$prefix);        // 输出模板内容        // 此时输出的就是最终页面内容  ob_get_clean();(模板编译完, php执行完)        $this->render($content,$charset,$contentType);        // 视图结束标签 无        Hook::listen('view_end');    }/**     * 解析和获取模板内容 用于输出     * @access public     * @param string $templateFile 模板文件名     * @param string $content 模板输出内容     * @param string $prefix 模板缓存前缀     * @return string     */    public function fetch($templateFile='',$content='',$prefix='') {        if(empty($content)) {            //自动定位模板文件            $templateFile   =   $this->parseTemplate($templateFile);            // 模板文件不存在直接返回            if(!is_file($templateFile)) E(L('_TEMPLATE_NOT_EXIST_').':'.$templateFile);        }else{            defined('THEME_PATH') or    define('THEME_PATH', $this->getThemePath());        }        // 页面缓存        ob_start();        ob_implicit_flush(0);        // 使用PHP原生模板    直接 extract include        if('php' == strtolower(C('TMPL_ENGINE_TYPE'))) {             $_content   =   $content;            // 模板阵列变量分解成为独立变量            extract($this->tVar, EXTR_OVERWRITE);            // 直接载入PHP模板            empty($_content)?include $templateFile:eval('?>'.$_content);        ////////  非原生,需要模板编译流程,不extract,而是传参        }else{                        // 视图解析标签            $params = array('var'=>$this->tVar,'file'=>$templateFile,'content'=>$content,'prefix'=>$prefix);            ///// Behavior\ParseTemplateBehavior 编译模板文件            Hook::listen('view_parse',$params);        }        // 获取并清空缓存        $content = ob_get_clean();        // 内容过滤标签        /**               'template_filter'=> array(                'Behavior\ContentReplaceBehavior', // 模板输出替换                ),         *      // 系统默认的特殊字符串替换,如js路径 css路径字符串         */        Hook::listen('view_filter',$content);        // 输出模板文件        return $content;    }/**     * 输出内容文本可以包括Html     * @access private     * @param string $content 输出内容     * @param string $charset 模板输出字符集     * @param string $contentType 输出类型     * @return mixed     */    private function render($content,$charset='',$contentType=''){        if(empty($charset))  $charset = C('DEFAULT_CHARSET');        if(empty($contentType)) $contentType = C('TMPL_CONTENT_TYPE');        // 网页字符编码        header('Content-Type:'.$contentType.'; charset='.$charset);        header('Cache-control: '.C('HTTP_CACHE_CONTROL'));  // 页面缓存控制        header('X-Powered-By:ThinkPHP');        // 输出模板文件        echo $content;    }

\ThinkPHP\Library\Behavior\ParseTemplateBehavior.class.php

/// 编译缓存实质是将模板文件的特定标签替换为php代码,自定义的拓展标签只能用在默认的thinkphp模板引擎。///此处不展开说明了class ParseTemplateBehavior {    // 行为扩展的执行入口必须是run    public function run(&$_data){        $engine             =   strtolower(C('TMPL_ENGINE_TYPE'));        $_content           =   empty($_data['content'])?$_data['file']:$_data['content'];        $_data['prefix']    =   !empty($_data['prefix'])?$_data['prefix']:C('TMPL_CACHE_PREFIX');        if('think'==$engine){ // 采用Think模板引擎            if((!empty($_data['content']) && $this->checkContentCache($_data['content'],$_data['prefix']))                 ||  $this->checkCache($_data['file'],$_data['prefix'])) { // 缓存有效                //载入模版缓存文件                ////////   =====  直接include file                Storage::load(C('CACHE_PATH').$_data['prefix'].md5($_content).C('TMPL_CACHFILE_SUFFIX'),$_data['var']);            }else{                $tpl = Think::instance('Think\\Template');                // 编译并加载模板文件                $tpl->fetch($_content,$_data['var'],$_data['prefix']);            }        }else{            // 调用第三方模板引擎解析和输出            if(strpos($engine,'\\')){                $class  =   $engine;            }else{                $class   =  'Think\\Template\\Driver\\'.ucwords($engine);                            }                        if(class_exists($class)) {                $tpl   =  new $class;                $tpl->fetch($_content,$_data['var']);            }else {  // 类没有定义                E(L('_NOT_SUPPORT_').': ' . $class);            }        }    }

五 收尾流程

简单流程

\ThinkPHP\Library\Think\App.class.php

// 应用结束标签        /**         *     'app_end'       =>  array(                'Behavior\ShowPageTraceBehavior', // 页面Trace显示                ),         */        Hook::listen('app_end');        return;

\ThinkPHP\Library\Think\Think.class.php

 // 脚本正常结束或者手动或异常终结处理 // 处理: 1.内存中的日志落地到文件 2.如果是异常终止,则输出错误 register_shutdown_function('Think\Think::fatalError');// 致命错误捕获    static public function fatalError() {        Log::save();        if ($e = error_get_last()) {            switch($e['type']){              case E_ERROR:              case E_PARSE:              case E_CORE_ERROR:              case E_COMPILE_ERROR:              case E_USER_ERROR:                  ob_end_clean();                self::halt($e);                break;            }        }    }

END

以后有机会再研究和补充此框架源码的机制实现说明,如:
Hook机制,controller反射机制,Model的ORM实现,路由解析定位的实现,模板的编译机制,session方法使用机制,文件编译缓存,PageTrace的使用等,欢迎大家一起探讨学习。

1 0
原创粉丝点击