ThinkPHP 3.2源码分析——从URL到系统设置

【一、架构简述】

可能一开始就开始分析源码的结构可能很多人不明白,那我就先大概说说thinkphp开发者的设计思路吧。大概可以分为这几个阶段:

1、依赖检测阶段

在这阶段里主要检测当前的环境是否满足要求,主要指php的版本等等。

2、参数加载阶段

在这个阶段主要是将目前框架所需要的参数 组成一个参数依赖数组。方便程序统一调用。

3、系统运行设置

该阶段会依赖加载的参数进行系统的全面设置

4、加载切片函数和用户自定义控制器模块

在这一阶段由于thinkphp多切片的原理,使得框架可以在很多阶段插入程序钩子,相信钩子很多人并不陌生。


【二、tp3.2的依赖检测阶段】

在index.php里面主要执行的就是依赖的检测,首先执行的就是php版本检测

// 检测PHP环境,如果php版本小于5.3程序终止
if(version_compare(PHP_VERSION,'5.3.0','<')) 
{
	die('require PHP > 5.3.0 !');
} 

这部分就这么结束了!


【三、tp3.2的参数加载阶段】

首先先定义app_debug模式,方便快速修改

// 开启调试模式 建议开发阶段开启 部署阶段注释或者设为false
define('APP_DEBUG',True);

再定义Application目录位置

// 定义应用目录
define('APP_PATH','./Application/');

最后引入thinkphp入口文件,index.php就算结束了。

// 引入ThinkPHP入口文件
require './ThinkPHP/ThinkPHP.php';

接下来是ThinkPHP.php开始执行

<?php

// 记录开始运行时间
$GLOBALS['_beginTime'] = microtime(TRUE);
// 这里记录内存的使用,通过的检测函数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('<?php '.$content));
}
else
{
  // 调试模式加载系统默认的配置文件
  C(include THINK_PATH.'Conf/debug.php');
  // 读取应用调试配置文件
  if(is_file(CONF_PATH.'debug.php'))
  {
  	C(include CONF_PATH.'debug.php');      
  }    
}
//---------------------------
// 读取当前应用状态对应的配置文件
// 这里也是加载文件,不过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

本科生毕业设计 基于Thinkphp3.2的毕设选题系统 摘 要 目前,大部分高校已开始应用较为完善的管理系统,如教务管理系统、学生选课管理系统等信息化管理系统,但是针对于学生毕业设计的相关管理操作,部分院校仍使用传统的工作模式,为提高管理的效率,我们设计开发“毕业设计网上管理系统”。该系统基于B/S架构进行设计搭建,整体开发使用MVC设计模式,所使用的动态网页开发语言为时下流行的PHP语言,前台采用HTML5、CSS3即Media Query技术开发的管理系统,B/S架构避免了因操作系统差异而导致的跨平台问题,MVC设计模式使得逻辑操作、数据处理和页面展示相分离,在一定程度上提升系统开发效率。 本系统由3大模块构成,分别是系统管理模块、教师操作模块以及学生操作模块。系统管理模块可对教师、学生个人信息和消息进行管理,对教师申报课题进行审核,并且可发送系统消息,便于通知;教师操作模块可以申报课题,浏览已通过审核课题的学生选题状况,查看已选题学生的进度情况及消息通知;学生操作模块可以选题,查看该课题教师可公布的联系方式,对已选但未确定的课题进行退选操作,浏览课题情况,提交毕设进度及消息通知等。该系统的使用将提高毕业设计这一环节的工作效率。 关键词:B/S架构;MVC设计框架;毕业设计选题;管理系统 Abstract Nowadays, most colleges and universities have been in use for more perfect management system, such as educational administration management system, student course selection management system of information management system, but in view of the student of graduation design related management operation, some colleges and universities are still using the traditional working mode, to provide the efficiency of information management, we designed and developed "graduation design online management system". The system based on B / S architecture was designed and built. The overall development using the MVC design pattern, the use of dynamic web development language too popular PHP language, the front desk using the HTML 5 and CSS 3 Media Query technology development management system management system, B/S structure to avoid the problem caused by different operating system cross-platform, MVC design pattern makes the logic operation, data processing and the page display phase separation, in a certain extent, improve the efficiency of system development. This system has three modules, respectively, the administrator module, teacher module and student module. Administrators can manage personal information and message to teachers and students, the teachers declare project audit, and can send messages to inform; Teacher module can declare topic, browse has passed the audit subject subject status of students, view has bee
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值