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
整体流程
红色的时钩子,蓝色为配置文件或函数定义文件
一 初始化
主要文件加载
hinkphp_3.2.3index.php
hinkphp_3.2.3ThinkPHPThinkPHP.php
hinkphp_3.2.3ThinkPHPLibraryThinkThink.class.php
hinkphp_3.2.3ThinkPHPLibraryThinkStorage.class.php
hinkphp_3.2.3ThinkPHPLibraryThinkStorageDriverFile.class.php
简单流程
index.php
// 检测PHP环境
if(version_compare(PHP_VERSION,"5.3.0","<")) die("require PHP > 5.3.0 !");
// 开启调试模式 建议开发阶段开启 部署阶段注释或者设为falseW
define("APP_DEBUG",True);
// 定义应用目录
define("APP_PATH","./Application/");
// 引入ThinkPHP入口文件
require "./ThinkPHP/ThinkPHP.php";
ThinkPHPThinkPHP.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;
// 应用初始化
ThinkThink::start();
ThinkPHPLibraryThinkThink.class.php
static public function start() {
// 注册AUTOLOAD方法
// 主要时去系统目录找类文件,或从应用配置文件注册的命名空间路径找
// array("Think","Org","Behavior","Com","Vendor")
// C("AUTOLOAD_NAMESPACE");
// 兼容处理不开命名空间的项目
spl_autoload_register("ThinkThink::autoload");
// 设定系统错误和异常处理函数注册
// 脚本正常结束或者手动或异常终结处理
// 处理: 1.内存中的日志落地到文件 2.如果是异常终止,则输出错误
register_shutdown_function("ThinkThink::fatalError");
//系统致命错误处理
//级别_USER_ERROR的写日志,页面输出提示
//低级别WARN NOTICE 等输出PageTrace,不影响脚本
set_error_handler("ThinkThink::appError");
//系统抛出异常处理
//写日志,页面输出
set_exception_handler("ThinkThink::appException");
// 初始化文件存储方式
Storage::connect(STORAGE_TYPE);
ThinkPHPLibraryThinkStorage.class.php
ThinkPHPLibraryThinkStorageDriverFile.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);
}
}
}
二 应用模式的编译缓存和配置文件加载
主要文件加载
hinkphp_3.2.3ThinkPHPModecommon.php
hinkphp_3.2.3ThinkPHPCommonfunctions.php
hinkphp_3.2.3ThinkPHPLibraryThinkHook.class.php
简单流程
ThinkPHPLibraryThinkThink.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 .= "
namespace { Think\Think::addMap(".var_export(self::$_map,true).");";
$content .= "
L(".var_export(L(),true).");
C(".var_export(C(),true).");ThinkHook::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解析,定位模块控制器和操作
主要文件加载
hinkphp_3.2.3ThinkPHPLibraryThinkApp.class.php
hinkphp_3.2.3ThinkPHPLibraryThinkDispatcher.class.php
hinkphp_3.2.3ThinkPHPLibraryThinkRoute.class.php
简单流程
ThinkPHPLibraryThinkThink.class.php
// 运行应用
App::run();
ThinkPHPLibraryThinkApp.class.php
/**
* 运行应用实例 入口文件使用的快捷方法
* @access public
* @return void
*/
static public function run()
{
// 应用初始化标签
/**
"app_init" => array(
"BehaviorBuildLiteBehavior", // 生成运行Lite文件
),
* 生成lite文件,用于替换入口文件
*/
Hook::listen("app_init");
// app初始化,见init()
App::init();
// 应用开始标签
/**
* "app_begin" => array(
"BehaviorReadHtmlCacheBehavior", // 读取静态缓存
),
* 若打开静态缓存开关,则直接获取缓存文件,输出,终止脚本。
*/
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执行和模板解析
主要文件加载
hinkphp_3.2.3ThinkPHPLibraryThinkController.class.php
hinkphp_3.2.3ThinkPHPLibraryThinkView.class.php
hinkphp_3.2.3ApplicationHomeControllerIndexController.class.php
hinkphp_3.2.3ThinkPHPLibraryBehaviorParseTemplateBehavior.class.php
hinkphp_3.2.3ThinkPHPLibraryThinkTemplate.class.php
hinkphp_3.2.3ThinkPHPLibraryThinkTemplateTagLibCx.class.php
hinkphp_3.2.3ThinkPHPLibraryThinkTemplateTagLib.class.php
简单流程
ApplicationHomeControllerIndexController.class.php
namespace HomeController;
use ThinkController;
class IndexController extends Controller {
public function index(){
$this->display();
}
}
hinkphp_3.2.3ThinkPHPLibraryThinkController.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("ThinkView");
//控制器初始化
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);
}
// 此外此类还用魔术方法实现空操作的跳转和默认模板的调用
// 也实现了页面的重定向和自动跳转
ThinkPHPLibraryThinkView.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);
///// BehaviorParseTemplateBehavior 编译模板文件
Hook::listen("view_parse",$params);
}
// 获取并清空缓存
$content = ob_get_clean();
// 内容过滤标签
/**
"template_filter"=> array(
"BehaviorContentReplaceBehavior", // 模板输出替换
),
* // 系统默认的特殊字符串替换,如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;
}
ThinkPHPLibraryBehaviorParseTemplateBehavior.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);
}
}
}
五 收尾流程
简单流程
ThinkPHPLibraryThinkApp.class.php
// 应用结束标签
/**
* "app_end" => array(
"BehaviorShowPageTraceBehavior", // 页面Trace显示
),
*/
Hook::listen("app_end");
return;
ThinkPHPLibraryThinkThink.class.php
// 脚本正常结束或者手动或异常终结处理
// 处理: 1.内存中的日志落地到文件 2.如果是异常终止,则输出错误
register_shutdown_function("ThinkThink::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;
}
}
}
以后有机会再研究和补充此框架源码的机制实现说明,如:
Hook机制,controller反射机制,Model的ORM实现,路由解析定位的实现,模板的编译机制,session方法使用机制,文件编译缓存,PageTrace的使用等,欢迎大家一起探讨学习。
- 上一篇: thinkphp3.2【自动加载】
- 下一篇: thinkphp3.2源码 ----- 自动加载
