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的使用等,欢迎大家一起探讨学习。
- ThinkPHP3.2.3源码分析一之系统流程
- ThinkPHP3.1.3源码分析(一) 入口文件分析
- Android 系统源码分析之View(一)
- Activity启动流程源码分析之入门(一)
- Service启动流程源码分析之startService(一)
- Thinkphp3.2.3执行流程
- Kubelet源码分析(一) 启动流程分析
- ThinkPHP3.2.3-文章管理系统-附带源码地址
- CSipSimple源码分析(一)之代码流程和用户注册流程
- 云客Drupal8源码分析之配置系统Configuration(一)
- glibc源码分析之系统调用(一)
- HBase1.0.0源码分析之请求处理流程分析以Put操作为例(一)
- HBase源码分析之HRegion上compact流程分析(一)
- Android源码分析---系统开机流程
- thinkphp3.2【框架执行流程分析】
- Android View系统源码分析(一)——概述&触摸事件总体处理流程
- WebRTC源码分析一:音频处理流程
- Elasticsearch源码分析(一)启动流程
- iOS 开发之余利宝接入指南
- Linux服务器运维常用命令
- 408 《操作系统》 第一章 操作系统概述
- 港澳通行证
- 安装elastic
- ThinkPHP3.2.3源码分析一之系统流程
- 密码学之加密算法简记
- 后缀树简单整理-上
- 可变参数,静态导入
- CentOS 7 上systemctl 的用法(注意不是多个容器服务开机启动)
- Swift 数组详细用法
- 算法学习之动态规划(leetcode 174. Dungeon Game)
- Eclipse环境下配置django
- 并查集-hdu1272