Thinkphp
Download
1 / 20

ThinkPHP 执行流程分析 - PowerPoint PPT Presentation


  • 88 Views
  • Uploaded on

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

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about ' ThinkPHP 执行流程分析' - kelly-caldwell


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.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
Thinkphp

ThinkPHP执行流程分析

[email protected]

http://www.yhustc.com


前言

  • 要分析什么?

    分析一下从访问index.php到加载Action文件、调用用户指定操作的过程。

  • 达到什么样的效果?

    分析一下在进入自己指定的模块与操作之前发生过一些什么事情,以及如何通过index.php进入指定的模块与操作,让你能搞明白这些事情,要的就这效果。


入口文件的定义

<?php

define('THINK_PATH', '../ThinkPHP');

//定义项目名称,如果不定义,默认为入口文件名称

define('APP_NAME', 'web');

define('APP_PATH', './web');

//加载ThinkPHP框架公共入口文件

require(THINK_PATH.'/ThinkPHP.php');

//实例化一个网站应用实例

$App = new App();

//执行应用程序

$App->run();

?>


Thinkphp php
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完成


Thinkphp php 32 else
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


Thinkphp lib think core app
进入ThinkPHP\Lib\Think\Core\App

入口文件中调用的$App->run()

public function run() {

$this->init();

$this->exec();

return ;

}

$App->run()返回的时候,对一个请求的处理就算完成了


App class php init 1
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行)


App class php init 2
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');

}

}


App class php init 3
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)看看


App class php init 4
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返回


Build
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


App class php loadplugin
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


Dispatcher class php dispatch
Dispatcher.class.php的dispatch

(Dispatcher.class.php的第一个函数就是dispatch)

检查当前URL模式URL_MODEL

如果存在$_GET变量,则根据当前的URL模式和设置进行重定向

进行路由定义检测

分析PATH_INFO的URL信息到数组

这一部分不难理解,就不详细讲代码了

URL解析完成,继续执行init


App class php getmodule
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); // [email protected]_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


App class php getaction
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


App class php exec 1
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中定义,[email protected]

}else{

$module = A(MODULE_NAME);

}

如果Action控制器不存在则检查空模块EmptyAction (527-534行)


App class php exec 2
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}();

}

}


App class php exec 3
App.class.php的exec函数(3)

执行应用结束过滤器(557行)

apply_filter('app_end');

写入错误日志(560-561行)

if(C('WEB_LOG_RECORD'))

Log::save();

exec执行完毕,一个请求处理完成了


回到入口文件

<?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两步

?>


小结

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


ad