可能一开始就开始分析源码的结构可能很多人不明白,那我就先大概说说thinkphp开发者的设计思路吧。大概可以分为这几个阶段:
1、依赖检测阶段
在这阶段里主要检测当前的环境是否满足要求,主要指php的版本等等。
2、参数加载阶段
在这个阶段主要是将目前框架所需要的参数 组成一个参数依赖数组。方便程序统一调用。
3、系统运行设置
该阶段会依赖加载的参数进行系统的全面设置
4、加载切片函数和用户自定义控制器模块
在这一阶段由于thinkphp多切片的原理,使得框架可以在很多阶段插入程序钩子,相信钩子很多人并不陌生。
在index.php里面主要执行的就是依赖的检测,首先执行的就是php版本检测
// 检测PHP环境,如果php版本小于5.3程序终止
if(version_compare(PHP_VERSION,'5.3.0','<'))
{
die('require PHP > 5.3.0 !');
}
这部分就这么结束了!
首先先定义app_debug模式,方便快速修改
// 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false
define('APP_DEBUG',True);
再定义Application目录位置
// 定义应用目录
define('APP_PATH','./Application/');
最后引入thinkphp入口文件,index.php就算结束了。
// 引入ThinkPHP入口文件
require './ThinkPHP/ThinkPHP.php';
接下来是ThinkPHP.php开始执行
// 这里记录内存的使用,通过的检测函数memory_get_usage是否存在
// 如果函数可用将内存开始使用的位置保存全局函数 GLOBALS 数组中
define('MEMORY_LIMIT_ON',function_exists('memory_get_usage'));
if(MEMORY_LIMIT_ON)
{
$GLOBALS['_startUseMems'] = memory_get_usage();
}
//-----------------------------------------
//------接下来开始疯狂的定义框架的设置参数-------
//-----------------------------------------
// 定义tp框架的版本信息
const THINK_VERSION = '3.2.0';
// 定义 URL 模式
const URL_COMMON = 0; //普通模式
const URL_PATHINFO = 1; //PATHINFO模式
const URL_REWRITE = 2; //REWRITE模式
const URL_COMPAT = 3; // 兼容模式
// 设置类文件的后缀
const EXT = '.class.php';
//-------------------------
// 定义 THINK_PATH 的文件目录
//-------------------------
if(!defined('THINK_PATH'))
{
define('THINK_PATH', __DIR__.'/');
}
//--------------------
//定义 APP_PATH 文件路径
//---------------------
if(!defined('APP_PATH') )
{
define('APP_PATH', dirname($_SERVER['SCRIPT_FILENAME']).'/');
}
//------------------------
// 应用状态 加载对应的配置文件
//-------------------------
if(!defined('APP_STATUS'))
{
define('APP_STATUS', '');
}
//-----------
// 是否调试模式
//------------
if(!defined('APP_DEBUG'))
{
define('APP_DEBUG', false);
}
//----------------
// 自动识别SAE环境
// ps: sae环境我曾询问过了解的大牛们,用的其实不多,主要就是新浪的服务器可忽略;
// 在这里如果不是sae环境(阿里云,腾讯云基本都不是)系统会定义APP的运行模式为common
// 缓存文件的储存方式定义为 File
//-----------------
if(function_exists('saeAutoLoader'))
{
defined('APP_MODE') or define('APP_MODE', 'sae');
defined('STORAGE_TYPE') or define('STORAGE_TYPE', 'Sae');
}
else
{
defined('APP_MODE') or define('APP_MODE', 'common'); // 应用模式 默认为普通模式
defined('STORAGE_TYPE') or define('STORAGE_TYPE', 'File'); // 存储类型 默认为File
}
//-----------------
// 定义系统运行时目录
//------------------
if(!defined('RUNTIME_PATH'))
{
define('RUNTIME_PATH', APP_PATH.'Runtime/');
}
//----------------
// 系统核心类库目录
//-----------------
if(!defined('LIB_PATH'))
{
define('LIB_PATH', realpath(THINK_PATH.'Library').'/');
}
//--------------
// Think类库目录
//---------------
if(!defined('CORE_PATH'))
{
define('CORE_PATH', LIB_PATH.'Think/');
}
//-------------
// 行为类库目录
// 就是钩子所在的目录
//--------------
if(!defined('BEHAVIOR_PATH'))
{
define('BEHAVIOR_PATH', LIB_PATH.'Behavior/');
}
//----------------------------------
// 由于定义的太多了请允许我偷懒一下,哈哈
// 大家不妨可以纸笔记录下定义的内容方便后面快速查看
// 在这里解释一下 or 在其前面的函数如果返回false 则执行后者,如果前者为true则后者不执行
//----------------------------------
// 系统应用模式目录
defined('MODE_PATH') or define('MODE_PATH', THINK_PATH.'Mode/');
// 第三方类库目录
defined('VENDOR_PATH') or define('VENDOR_PATH', LIB_PATH.'Vendor/');
// 应用公共目录
defined('COMMON_PATH') or define('COMMON_PATH', APP_PATH.'Common/');
// 应用配置目录
defined('CONF_PATH') or define('CONF_PATH', COMMON_PATH.'Conf/');
// 应用语言目录
defined('LANG_PATH') or define('LANG_PATH', COMMON_PATH.'Lang/');
// 应用静态目录
defined('HTML_PATH') or define('HTML_PATH', APP_PATH.'Html/');
// 应用日志目录
defined('LOG_PATH') or define('LOG_PATH', RUNTIME_PATH.'Logs/');
// 应用缓存目录
defined('TEMP_PATH') or define('TEMP_PATH', RUNTIME_PATH.'Temp/');
// 应用数据目录
defined('DATA_PATH') or define('DATA_PATH', RUNTIME_PATH.'Data/');
// 应用模板缓存目录
defined('CACHE_PATH') or define('CACHE_PATH', RUNTIME_PATH.'Cache/');
//----------------------
// 系统信息:设置系统是否将传来的数据中的'"\加上反斜线参数
//-----------------------
if(version_compare(PHP_VERSION,'5.4.0','<'))
{
ini_set('magic_quotes_runtime',0);
define('MAGIC_QUOTES_GPC',get_magic_quotes_gpc()?True:False);
}
else
{
define('MAGIC_QUOTES_GPC',false);
}
//------------
//是否为CGI模式
//------------
define('IS_CGI',substr(PHP_SAPI, 0,3)=='cgi' ? 1 : 0 );
//----------------
//是否为windows环境
//----------------
define('IS_WIN',strstr(PHP_OS, 'WIN') ? 1 : 0 );
//----------------
//是否为CLI模式
//----------------
define('IS_CLI',PHP_SAPI=='cli'? 1 : 0);
//-----------------------
// 如果是CGI模式(网页模式)定义_PHP_FILE_
// 参数$_SERVER可参考 https://zhidao.baidu.com/question/99055936.html
// 这里的 _PHP_FILE_ 框架不改造的情况下通常输出的为 /index.php
//-----------------------
if(!IS_CLI) {
// 当前文件名
if(!defined('_PHP_FILE_'))
{
if(IS_CGI)
{
//CGI/FASTCGI模式下
$_temp = explode('.php',$_SERVER['PHP_SELF']);
define('_PHP_FILE_', rtrim(str_replace($_SERVER['HTTP_HOST'],'',$_temp[0].'.php'),'/'));
}
else
{
define('_PHP_FILE_', rtrim($_SERVER['SCRIPT_NAME'],'/'));
}
}
//------------------------------------
//定义__ROOT__:根据_PHP_FILE_所在的文件夹
// 不改造的情况下通常为根目录
//------------------------------------
if(!defined('__ROOT__')) {
$_root = rtrim(dirname(_PHP_FILE_),'/');
define('__ROOT__', (($_root=='/' || $_root=='\\')?'':$_root));
}
}
//----------------
// 到这里大部分的框架设置工作已经完成了
// 开始加载核心Think类
//----------------
require CORE_PATH.'Think'.EXT;
//----------
// 应用初始化
//-----------
Think\Think::start();
该配置为thinkphp的核心设计,下面我们一步步解读:
//----------------------------------------------------------
//首先架构设计了自动加载类的映射和实例化对象。使得系统更好的复用类和对象。
//------------------------------------------------------------
// 类映射数组,稍后会详细介绍,这里知道有这个存在就行
private static $_map = array();
// 实例化对象,符合设计原则的单例模式。
private static $_instance = array();
由于上一章节最后程序是调用的 Think\Think::start();函数,下面我们从该函数开始:
//-------------------
// 注册AUTOLOAD方法
//由于该方法我之前写过一篇文章,不再讲解
//------贴出连接-------
//https://blog.csdn.net/weixin_44187959/article/details/91864086
//--------------------
spl_autoload_register('Think\Think::autoload');
//----------------------------
// 设定错误导致程序退出时的处理规则
// 如果有对错误处理不太了解的可以参考我下面的连接。
// https://blog.csdn.net/weixin_44187959/article/details/90750562
//----------------------------
register_shutdown_function('Think\Think::fatalError');
// 贴出方法内容,tp3对错误的处理根据错误级别处理包含,存入日志,报出行号、错误信息等等。
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;
}
}
}
//-------------------------------------
// 设置发生错误时候 程序所做的处理,方法和shutdown方法类似,输出错误信息和行号等等。
//-------------------------------------
set_error_handler('Think\Think::appError');
//贴出处理的顺序,不做过多的解读,应该很简单,大家都看得懂。
static public function appError($errno, $errstr, $errfile, $errline) {
switch ($errno) {
case E_ERROR:
case E_PARSE:
case E_CORE_ERROR:
case E_COMPILE_ERROR:
case E_USER_ERROR:
ob_end_clean();
$errorStr = "$errstr ".$errfile." 第 $errline 行.";
if(C('LOG_RECORD')) Log::write("[$errno] ".$errorStr,Log::ERR);
self::halt($errorStr);
break;
case E_STRICT:
case E_USER_WARNING:
case E_USER_NOTICE:
default:
$errorStr = "[$errno] $errstr ".$errfile." 第 $errline 行.";
self::trace($errorStr,'','NOTIC');
break;
}
}
//--------------
//最后是处理异常
//---------------
set_exception_handler('Think\Think::appException');
//同样贴出方法,具体过程也不多说,大家都看得懂。
static public function appException($e) {
$error = array();
$error['message'] = $e->getMessage();
$trace = $e->getTrace();
if('E'==$trace[0]['function']) {
$error['file'] = $trace[0]['file'];
$error['line'] = $trace[0]['line'];
}else{
$error['file'] = $e->getFile();
$error['line'] = $e->getLine();
}
$error['trace'] = $e->getTraceAsString();
Log::record($error['message'],Log::ERR);
// 发送404信息
header('HTTP/1.1 404 Not Found');
header('Status:404 Not Found');
self::halt($error);
}
//-----------------
// 初始化文件存储方式,这里的STORAGE_TYPE已经定义为File
//-----------------
Storage::connect(STORAGE_TYPE);
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//这里属于延伸赘述一下Storage这个类
//这个类的作用是通过不同的加载方式,读取不同加载方式类内部的方法
//很符合tp的多驱动方式设计模式,很多类似facede这种实现都依赖这种方法
//-----------------
static protected $handler;
static public function connect($type,$options=array()) {
$class = 'Think\\Storage\\Driver\\'.ucwords($type);
self::$handler = new $class($options);
}
static public function __callstatic($method,$args){
$type=end($args);
$method_type=$method.ucfirst($type);
if(method_exists(self::$handler, $method_type)){
return call_user_func_array(array(self::$handler,$method_type), $args);
}
//调用缓存类型自己的方法
if(method_exists(self::$handler, $method)){
return call_user_func_array(array(self::$handler,$method), $args);
}
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
//--------------
//定义缓存文件目录
//--------------
$runtimefile = RUNTIME_PATH.APP_MODE.'~runtime.php';
//-------------------------------------------
//在 debug关闭(生产环境),并且缓存文件存在的情况下,会读取缓存
//--------------------------------------------
if(!APP_DEBUG && Storage::has($runtimefile))
{
Storage::load($runtimefile);
}
//------------------------------
//如果不是上述的条件下会执行如下的操作
//------------------------------
//----------------------------------
//删除旧的缓存文件,定义缓存字符串content
//-----------------------------------
if(Storage::has($runtimefile))
{
Storage::unlink($runtimefile);
}
$content = '';
//----------------------------
//这里的mode 为加载的参数数组
//如果用户app目录下没有定义自己的设置目录,默认加载系统的设置目录。
//----------------------------
$mode = include is_file(CONF_PATH.'core.php')?CONF_PATH.'core.php':MODE_PATH.APP_MODE.'.php';
//---------------------
//在这里贴出系统的设置参数
//--------------------
'config' => array(
THINK_PATH.'Conf/convention.php', // 系统惯例配置
CONF_PATH.'config.php', // 应用公共配置
),
// 别名定义
'alias' => array(
'Think\Log' => CORE_PATH . 'Log'.EXT,
'Think\Log\Driver\File' => CORE_PATH . 'Log/Driver/File'.EXT,
'Think\Exception' => CORE_PATH . 'Exception'.EXT,
'Think\Model' => CORE_PATH . 'Model'.EXT,
'Think\Db' => CORE_PATH . 'Db'.EXT,
'Think\Template' => CORE_PATH . 'Template'.EXT,
'Think\Cache' => CORE_PATH . 'Cache'.EXT,
'Think\Cache\Driver\File' => CORE_PATH . 'Cache/Driver/File'.EXT,
'Think\Storage' => CORE_PATH . 'Storage'.EXT,
),
// 函数和类文件
'core' => array(
THINK_PATH.'Common/functions.php',
COMMON_PATH.'Common/function.php',
CORE_PATH . 'Hook'.EXT,
CORE_PATH . 'App'.EXT,
CORE_PATH . 'Dispatcher'.EXT,
//CORE_PATH . 'Log'.EXT,
CORE_PATH . 'Route'.EXT,
CORE_PATH . 'Controller'.EXT,
CORE_PATH . 'View'.EXT,
BEHAVIOR_PATH . 'ParseTemplateBehavior'.EXT,
BEHAVIOR_PATH . 'ContentReplaceBehavior'.EXT,
),
// 行为扩展定义
'tags' => array(
'app_begin' => array(
'Behavior\ReadHtmlCache', // 读取静态缓存
),
'app_end' => array(
'Behavior\ShowPageTrace', // 页面Trace显示
),
'view_parse' => array(
'Behavior\ParseTemplate', // 模板解析 支持PHP、内置模板引擎和第三方模板引擎
),
'template_filter'=> array(
'Behavior\ContentReplace', // 模板输出替换
),
'view_filter' => array(
'Behavior\WriteHtmlCache', // 写入静态缓存
),
)
//------------
// 加载系统核心文件
// debug关闭情况下,编译缓存
//------------
foreach ($mode['core'] as $file)
{
if(is_file($file)) {
include $file;
if(!APP_DEBUG) $content .= compile($file);
}
}
//------------------
// 加载应用模式配置文件
// 解释一下C函数:
// 如果参数1为数组,则合并设置数组
// 如果参数1为字符串,则获取对应的参数值
// 如果参数1为字符串,参数二有值,则为更新字符串在数组中的值
//-------------------
foreach ($mode['config'] as $key=>$file){
is_numeric($key)?C(include $file):C($key,include $file);
}
//--------------------------
// 读取当前应用模式对应的配置文件
// 如果系统的运行模式不是common模式并且对应的config存在,则加载设置到系统设置组
//---------------------------
if('common' != APP_MODE && is_file(CONF_PATH.'config_'.APP_MODE.'.php'))
{
C(include CONF_PATH.'config_'.APP_MODE.'.php');
}
//----------------
// 加载模式别名定义
// 在本章节前我们提到的类的映射数组,该函数是将别名映射到类的路径,简化实例
// 如果用户有自定义的别名数组,也加载到映射中。
//----------------
//系统设置
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');
}
//--------------------
// 加载框架底层语言包
// 通过C函数 获取 系统的默认语言设置,加载对应文件
// 这里的L函数只要将语言映射 保存在静态数组中,支持数组和字符串单映射
//---------------------
L(include THINK_PATH.'Lang/'.strtolower(C('DEFAULT_LANG')).'.php');
//--------------------
// 这里主要是区别生产环境和开发环境
// 如果是生产环境(debug=false),则储存缓存
// 如果是开发环境(debug=true), 则加载debug系统配置文件和用户自定义的配置文件
//--------------------
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('
//---------------------------
// 读取当前应用状态对应的配置文件
// 这里也是加载文件,不过APP_STATUS我记得前面设置为 null 所以一般不会加载这个配置
//---------------------------
if(APP_STATUS && is_file(CONF_PATH.APP_STATUS.'.php'))
{
C(include CONF_PATH.APP_STATUS.'.php');
}
//--------------
// 设置系统时区
//--------------
date_default_timezone_set(C('DEFAULT_TIMEZONE'));
//--------------------------------
// 检查应用目录结构 如果不存在则自动创建
// 这里主要是创建一些日志和缓存目录,可自行查阅源码不多赘述
//--------------------------------
if(C('CHECK_APP_DIR') && !is_dir(LOG_PATH)) {
// 创建应用目录结构
require THINK_PATH.'Common/build.php';
}
//-----------------------------------
// 这里记录了所有文件的加载时间并且启动app
//-----------------------------------
// 记录加载文件时间
G('loadTime');
// 运行应用
App::run();
从url输入到系统的配置我们就全部说完了,接下来的内容请查看我的另一篇文章;
ThinkPHP 3.2源码分析——系统的App的执行流程;
https://blog.csdn.net/weixin_44187959/article/details/93604224