1 / 20

ThinkPHP 执行流程分析

ThinkPHP 执行流程分析. yhustc@ThinkPHP http://www.yhustc.com. 前言. 要分析什么? 分析一下从访问 index.php 到加载 Action 文件、调用用户指定操作的过程。 达到什么样的效果? 分析一下在进入自己指定的模块与操作之前发生过一些什么事情,以及如何通过 index.php 进入指定的模块与操作,让你能搞明白这些事情,要的就这效果。. 入口文件的定义. <?php define('THINK_PATH', '../ThinkPHP'); // 定义项目名称,如果不定义,默认为入口文件名称

Download Presentation

ThinkPHP 执行流程分析

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. ThinkPHP执行流程分析 yhustc@ThinkPHP http://www.yhustc.com

  2. 前言 • 要分析什么? 分析一下从访问index.php到加载Action文件、调用用户指定操作的过程。 • 达到什么样的效果? 分析一下在进入自己指定的模块与操作之前发生过一些什么事情,以及如何通过index.php进入指定的模块与操作,让你能搞明白这些事情,要的就这效果。

  3. 入口文件的定义 <?php define('THINK_PATH', '../ThinkPHP'); //定义项目名称,如果不定义,默认为入口文件名称 define('APP_NAME', 'web'); define('APP_PATH', './web'); //加载ThinkPHP框架公共入口文件 require(THINK_PATH.'/ThinkPHP.php'); //实例化一个网站应用实例 $App = new App(); //执行应用程序 $App->run(); ?>

  4. ThinkPHP.php里面的干了些啥 记录开始执行时间 $GLOBALS[‘_beginTime’](20行) 检测 THINK_PATH、 APP_NAME、 APP_PATH、 RUNTIME_PATH 定义,如果没有则创建(23-26行) //不知道你发现没有,以index.php这基准,通过APP_PATH,可以组装出任意路径 检查是否有核心缓存~runtime.php(28行) if(file_exists(RUNTIME_PATH.'~runtime.php')) { // 加载框架核心缓存文件 // 如果有修改核心文件请删除该缓存 require RUNTIME_PATH.'~runtime.php'; }else{ 进入else } 记录加载文件时间 $GLOBALS['_loadTime']; require thinkphp.php完成

  5. ThinkPHP.php 32行的else 加载系统定义文件 ThinkPHP\Common\defines.php 和公共函数文件 ThinkPHP\Common\functions.php(34-36行) 如果项目编译缓存目录不存在,则自动创建项目目录结构(41行buildAppDir()是在functions.php中定义的函数) 加载系统核心类库(包括ThinkPHP\LIB\THINK下级各子目录下的 Base、App、Action、Model、View、ThinkException、Log)(45-54行) 如果 PHP 版本低于 5.2.0 则加载兼容函数库 ThinkPHP\Common\compat.php(76行) 生成核心编译缓存~runtime.php(生成在APP_PATH\ Temp\目录下) 退出else

  6. 进入ThinkPHP\Lib\Think\Core\App 入口文件中调用的$App->run() public function run() { $this->init(); $this->exec(); return ; } $App->run()返回的时候,对一个请求的处理就算完成了

  7. App.class.php的init函数(1) 设定错误和异常处理机制(set_error_handler和set_exception_handler)(92-93行) 项目预编译并载入(97-103行) if(file_exists(RUNTIME_PATH.'~app.php') && filemtime(RUNTIME_PATH.'~app.php')>filemtime(CONFIG_PATH.'config.php')) { // 直接读取编译后的项目文件 C(include RUNTIME_PATH.'~app.php'); }else{ // 预编译项目 $this->build(); } 设置时区支持(106-107行) Session过滤器检查、 session初始化(109-115行)

  8. App.class.php的init函数(2) 检查并加载插件(118-120行) if(C('THINK_PLUGIN_ON')) { $this->loadPlugIn(); } URL分析和调度(126-135行) if(C('DISPATCH_ON')) { if( 'Think'== C('DISPATCH_NAME') ) { // 使用内置的ThinkDispatcher调度器 import('Think.Util.Dispatcher'); Dispatcher::dispatch(); }else{ // 加载第三方调度器 apply_filter('app_dispatch'); } }

  9. App.class.php的init函数(3) 取得模块和操作名称 如果有伪装 则返回真实的名称(145-146行) if(!defined('MODULE_NAME')) define('MODULE_NAME', $this->getModule()); // Module名称 if(!defined('ACTION_NAME')) define('ACTION_NAME', $this->getAction()); // Action操作 加载模块配置文件(149-151行) 页面防刷新机制检查(154-167行) 语言检查并读取对应的语言文件(170行) $this->checkLanguage(); //通过COOKIE保存当前语言,可以dump($_COOKIE)看看

  10. App.class.php的init函数(4) 模板检查并定义相关的模板变量(171行) $this->checkTemplate(); //通过COOKIE保存当前模板主题,可以dump($_COOKIE)看看 RBAC权限检测(173-184行) RBAC最好单独了解,这个PPT里写不了那么全面 如果开启静态写入则读取静态缓存文件(186-189行) 应用初始化过滤插件app_init (191行) apply_filter(‘app_init’); //ThinkPHP\Common\functions.php中有定义 记录应用初始化时间$GLOBALS['_initTime'] 初始化完成,init返回

  11. Build了一些什么东西? 加载系统惯例配置文件ThinkPHP\Common\convention.php(213行) 加载项目配置文件APP_PATH\Conf\config.php(217行) 加载项目公共文件APP_PATH\Common\common.php(224行) 如果是调试模式加载系统调试配置文件ThinkPHP\Common\debug.php(233行) 如果定义了项目的调试配置文件则载入APP_PATH\Common\debug.php(236行) 生成项目编译缓存文件APP_PATH\Temp\~app.php(242行) 现在知道~app.php里面放了些啥吧?留一个问题,如何让一段代码每次都自动执行,而且不受RBAC权限控制的限制? Build完毕,继续执行init

  12. App.class.php的loadplugin 加载有效插件文件(loadplugin函数在App.class.php 478行) if(file_exists(RUNTIME_PATH.'~plugins.php')) { include RUNTIME_PATH.‘~plugins.php’; //注意了,插件也会缓存,改了插件记得清缓存 }else{ // 检查插件数据 $common_plugins = get_plugins(THINK_PATH.'/PlugIns','Think');// 公共插件 $app_plugins = get_plugins();// 项目插件 ThinkPHP\Common\functions.php中有定义 // 合并插件数据 $plugins = array_merge($common_plugins,$app_plugins); // 缓存插件数据 $content = ''; foreach($plugins as $key=>$val) { include $val['file']; $content .= php_strip_whitespace($val['file']); } file_put_contents(RUNTIME_PATH.‘~plugins.php’,$content); //RUNTIME_PATH默认就是APP_PATH\Temp } 插件加载完毕,继续执行init

  13. Dispatcher.class.php的dispatch (Dispatcher.class.php的第一个函数就是dispatch) 检查当前URL模式URL_MODEL 如果存在$_GET变量,则根据当前的URL模式和设置进行重定向 进行路由定义检测 分析PATH_INFO的URL信息到数组 这一部分不难理解,就不详细讲代码了 URL解析完成,继续执行init

  14. App.class.php的getModule App.class.php 256行 检查 VAR_MODULE 变量(包括 GET 和 POST), 如果未定义,则获取默认模块名(258-262行) 检查组件模块 if(strpos($module,C(‘COMPONENT_DEPR’))) { // COMPONENT_DEPR 组件模式分隔符,默认是@ define(‘C_MODULE_NAME’,$module); // 带@的完整的模块名定义为C_MODULE_NAME $array = explode(C('COMPONENT_DEPR'),$module); $module = array_pop($array); // @后面部分作为实际的模块名称,返回后被定义为MODULE_NAME } 检查模块URL伪装 if(C('MODULE_REDIRECT')) { $res = preg_replace('@(\w+):([^,\/]+)@e', '$modules[\'\\1\']="\\2";', C('MODULE_REDIRECT')); if(array_key_exists($module,$modules)) { define(‘P_MODULE_NAME’,$module); // 伪装的模块名定义为P_MODULE_NAME $module = $modules[$module]; } } return $module; 模块名称解析完毕,也就是http://server/project/index.php/Model/Action/的Model部分,回到init

  15. App.class.php的getAction App.class.php 292行 检查 VAR_ACTION 变量(包括 GET 和 POST), 如果未定义,则获取默认操作名 检查操作链(以下部分可以参考getModule自行分析,别骂我,学会自己分析是有必要的) if(strpos($action,C('COMPONENT_DEPR'))) { // 记录完整的操作名 define('C_ACTION_NAME',$action); $array = explode(C('COMPONENT_DEPR'),$action); // 实际的模块名称 $action = array_pop($array); } 检查操作URL伪装 if(C('ACTION_REDIRECT')) { $res = preg_replace('@(\w+):([^,\/]+)@e', '$actions[\'\\1\']="\\2";', C('ACTION_REDIRECT')); if(array_key_exists($action,$actions)) { // 记录伪装的操作名称 define('P_ACTION_NAME',$action); $action = $actions[$action]; } } 操作名称解析完毕,也就是http://server/project/index.php/Model/Action/的Action部分,回到init

  16. App.class.php的exec函数(1) AUTO_LOAD_CLASS检查如果有则导入公共类(514-520行) 实例化当前模块的Action控制器类(522-526行) if(defined(‘C_MODULE_NAME’)) { //组件化模块 $module = A(C_MODULE_NAME); //A函数在ThinkPHP\Common\functions.php中定义,可以根据@连接的模块名自动处理找到正确的Action文件 }else{ $module = A(MODULE_NAME); } 如果Action控制器不存在则检查空模块EmptyAction (527-534行)

  17. App.class.php的exec函数(2) App.class.php 537-555行 $action = ACTION_NAME.C(‘ACTION_SUFFIX’); if(defined(‘C_ACTION_NAME’)) { // 定义了操作链,执行操作链,最多只能有一个输出(这个得由用户自己控制,注意写代码的时候不要多处使用display来输出,用echo没问题) $actionList = explode(C('COMPONENT_DEPR'),C_ACTION_NAME); foreach ($actionList as $action){ $module->$action(); //这里就是调用指定Action的指定function了 } }else{ //如果存在前置操作,首先执行 if (method_exists($module,'_before_'.$action)) { $module->{'_before_'.$action}(); } //执行操作 $module->{$action}(); //执行指定Action的指定function,这里面的流程我就没法给你分析了,看自己代码怎么写的 //如果存在后置操作,继续执行 if (method_exists($module,'_after_'.$action)) { $module->{'_after_'.$action}(); } }

  18. App.class.php的exec函数(3) 执行应用结束过滤器(557行) apply_filter('app_end'); 写入错误日志(560-561行) if(C('WEB_LOG_RECORD')) Log::save(); exec执行完毕,一个请求处理完成了

  19. 回到入口文件 <?php define('THINK_PATH', '../ThinkPHP'); //定义项目名称,如果不定义,默认为入口文件名称 define('APP_NAME', 'web'); define('APP_PATH', './web'); //加载ThinkPHP框架公共入口文件 require(THINK_PATH.'/ThinkPHP.php'); //实例化一个网站应用实例 $App = new App(); //执行应用程序 $App->run(); //分为init和exec两步 ?>

  20. 小结 $App->run()分为init和exec两部分,现在都已经走完,但是这中间还省略了一部分分析,比如RBAC,比如用户代码中的那些操作中,Model相关的代码。建议用户自行分析一下,这样有助于对TP的理解。有什么问题欢迎到ThinkPHP论坛(http://bbs.thinkphp.cn)或者我的Blog(http://www.yhustc.com)来交流。

More Related