执行流程
--------------------------------------------------------------------------------
我们对用户的第一次URL访问 http://<serverIp>/My/index.php/Index/show/ 所执行的流程进行详细的分析,用户的URL访问首先是定位到了My项目的index.php 入口文件(注意:如果使用了URL_REWRITE,可能index.php已经被隐藏了),项目的入口文件所做的其实是实例化一个App应用实例,并且执行这个应用。http://servername/appName/moduleName/actionName/params
来获取当前需要执行的项目(appName)、模块(moduleName)和操作(actionName),在某些情况下,appName可以不需要(通常是网站的首页,因为项目名称可以在入口文件中指定,这种情况下,appName就会被入口文件替代)
每个模块名称是一个Action文件,类似于我们平常所说的控制器,系统会自动寻找项目类库Action目录下面的相关类,如果没有找到,会尝试搜索应用目录下面的组件类中包含的模块类,如果依然没有,则抛出异常。
而actionName操作是首先判断是否存在Action类的公共方法,如果不存在则会继续寻找父类中的方法,如果依然不存在,则会检查是否存在空操作定义,如果还没有就会寻找是否存在自动匹配的模版文件。如果存在模版文件,那么就直接渲染模版输出。
因此应用开发中的一个重要过程就是给不同的模块定义具体的操作。一个应用如果不需要和数据库交互的时候可以不需要定义模型类,但是必须定义Action控制器。
Action控制器的定义非常简单,只要继承Action基础类就可以了,例如:http://servername/index.php/User/
http://servername/index.php/User/add
如果你需要增加或者重新定义自己的操作方法,增加一个方法就可以了,例如http://servername/index.php/User/select/了,系统会自动定位当前操作的模板文件。
默认模块和操作
------------------------------------------------------------------
http://serverName/index.php,没有带任何模块和操作的参数,系统就会寻找默认模块和默认操作,通过DEFAULT_MODULE和DEFAULT_ACTION来定义,系统的默认模块设置是Index模块,默认操作设置是index操作。也就是说
http://serverName/index.php和
http://serverName/index.php/Index以及
http://serverName/index.php/Index/index 等效。
入口文件
--------------------------------------------------------------------
1、加载公共入口文件
在实例化App类之前,我们需要首先加载系统的公共入口文件ThinkPHP.php,这个文件是ThinkPHP的总入口,让我们来一探究竟。在加载ThinkPHP.php文件的过程中,其实完成了下面的操作:
记录开始执行时间 $GLOBALS[\'_beginTime\'];
检测THINK_PATH定义,如果没有则创建;
检测项目名称APP_NAME,如果没有则按照一定规则自动定义;
检测项目编译缓存目录定义,没有则取项目的Temp目录;
加载系统定义文件defines.php和公共函数文件functions.php;
如果项目编译缓存目录不存在,则自动创建项目目录结构;
加载系统核心类库(包括Base、App、Action、Model、View、ThinkException、Log);
如果PHP版本低于5.2.0则加载兼容函数库compat.php;
生成核心编译缓存~runtime.php;
记录加载文件时间 $GLOBALS[\'_loadTime\'];
2、 项目初始化init
在加载完成ThinkPHP的公共入口文件之后,我们就开始执行应用了,而首先应该是初始化App应用。
设定错误和异常处理机制(set_error_handler和set_exception_handler);
项目预编译并载入;
设置时区支持;
Session过滤器检查;
session初始化;
检查并加载插件;
URL分析和调度;
获取当前执行的模块和操作名;
加载模块配置文件;
页面防刷新机制检查;
语言检查并读取对应的语言文件;
模板检查并定义相关的模板变量;
RBAC权限检测;
如果开启静态写入则读取静态缓存文件;
应用初始化过滤插件 app_init;
记录应用初始化时间 $GLOBALS[\'_initTime\']
3、 项目预编译
加载系统惯例配置文件convention.php;
加载项目配置文件 config.php;
加载项目公共文件 common.php;
如果是调试模式加载系统调试配置文件 debug.php;
如果定义了项目的调试配置文件则载入 debug.php;
生成项目编译缓存文件~app.php;
4、 URL分析Dispatcher
检查当前URL模式URL_MODEL;
如果存在$_GET变量,则根据当前的URL模式和设置进行重定向;
进行路由定义检测;
分析PATH_INFO的URL信息到数组;
把PATH_INFO得到的值和$_GET合并;
5、 获取模块和操作名
检查VAR_MODULE变量(包括GET 和POST),如果未定义,则获取默认模块名;
检查组件模块;
检查模块伪装;
检查VAR_ACTION变量(包括GET 和POST),如果未定义,则获取默认操作名;
检查操作链;
检查操作伪装;
6、 项目执行exec
AUTO_LOAD_CLASS 检查 如果有则导入公共类;
实例化当前模块的Action控制器类;
如果Action控制器不存在则检查空模块 EmptyAction;
检查操作链,如果有执行操作链;
检查前置操作方法 _before_操作名;
执行模块的操作方法,调度转移给Action控制器;
执行后置操作方法 _after_操作名;
执行应用结束过滤器 app_end;
如果开启日志记录,写入错误日志;
7、 执行控制器的操作
实例化视图类View;
取得当前控制器名称;
控制器初始化_initialize;
如果操作方法不存在检查空操作 _empty;
如果空操作没有定义则检查对应的模板文件;
调用模型获取数据;
渲染视图进行输出;
8、 调用模型获取数据find
实例化模型类;
模型初始化 _initialize;
判断当前模型名称和对应数据表;
实例化数据库操作对象;
数据表字段检测并缓存;
查询需要的数据;
判断是否视图模型;
如果是延时查询返回ResultIterator对象;
取出数据对象的时候记录乐观锁;
获取文本字段数据;
获取关联数据;
对数据对象自动编码转换;
记录当前数据对象;
返回定义的数据格式(数组或者stdClass对象)
9、 输出视图
模板变量赋值;
检测是否是布局输出;
检测页面输出编码;
缓存初始化过滤 ob_init;
页面缓存开启ob_start;
缓存开启后执行的过滤;
模版文件名过滤 template_file;
定位当前输出的模板文件;
模版变量过滤 template_var;
根据不同模版引擎进行处理;
如果是PHP模板引擎,直接载入模板文件;
使用内置模板引擎,检测缓存有效期;
缓存无效则重新编译模板文件;
载入模板缓存文件;
获取并清空缓存;
输出编码转换;
输出过滤 ob_content;
开启静态写入则写入静态文件;
如果输出则获取视图运行时间;
如果是display则渲染模板输出信息;
开启页面Trace则显示页面Trace信息;
如果是fetch则返回模板输出信息;
项目编译
--------------------------------------------------------------------------
ThinkPHP v1.0.0正式版本开始引入了新的项目编译机制,而在1.0.3版本中又进行了一些优化设置。
所谓的项目编译机制是指系统第一次运行的时候会自动生成核心缓存文件~runtime.php和项目编译缓存文件~app.php,第二次就会直接载入编译过的缓存文件,从而省去很多IO开销,加快执行速度,并且编译缓存文件默认是去掉空白和注释的,因此存在一个预编译的过程。项目编译机制对运行没有任何影响,预编译操作和其他的目录检测机制只会执行一次,因此无论在预编译过程中做了多少复杂的操作,对后面的执行没有任何效率的缺失。
1.0.2版本之前默认的生成路径是项目的根目录,1.0.2版本开始,默认的编译缓存目录为TEMP_PATH (项目的临时文件目录)。
还可以在入口文件里面设置RUNTIME_PATH,例如:
- defined(\'RUNTIME_PATH\',\'./MyApp/runtime/\');
系统会自动把编译缓存文件放到该目录下面生成,注意这样的话在Linux环境下面需要对RUNTIME_PATH目录设置可写权限。
注意在调试模式下面不会生成项目编译缓存,但是依然会生成核心缓存。
如果不希望生成核心缓存文件的话,可以在项目入口文件里面设置 CACHE_RUNTIME
例如:
如果在非调试模式下面,修改了配置文件或者项目公共文件,需要删除项目编译文件。1.0.4以上版本修改配置文件会自动重新生成编译缓存文件。
1.0.3版本以上,还可以设置对编译缓存的内容是否进行去空白和注释,例如:
- define(\'STRIP_RUNTIME_SPACE\',false);
则生成的编译缓存文件是没有经过去注释和空白的,仅仅是把文件合并到一起,这样的好处是便于调试的错误定位,建议部署模式的时候把上面的设置为True或者删除该定义。
注意事项
下面的一些问题需要引起注意,便于在开发过程中更快的解决问题:
1、编译缓存目录必须设置为可写
2、如果有修改核心文件,可能需要删除核心编译缓存~runtime.php