Thinkphp 源码分析

ThinkPHP是国内非常火的一个轻量级框架,采用MVC模式,结构写的非常好,今天 大象 带大家走一下ThinkPHP框架系统运行流程,我将跟随ThinkPHP的执行进行代码讲解,第一课,先讲解URL路由解析

一、ThinkPHP框架目录

/test/index.php //项目入口文件

/ThinkPHP/ThinkPHP.php //框架入口文件

Common 框架公共文件目录(函数库)

ThinkPHP/Common/runtime.php //框架初次运行文件

ThinkPHP/Common/common.php //框架基础函数库

ThinkPHP/Common/functions.php //标准模式公共函数库

Conf 框架配置文件目录

ThinkPHP/Conf/convention.php  //惯例配置文件,系统默认配置小于项目配置

ThinkPHP/Conf/debug.php //默认的调试模式配置文件

Lang 框架系统语言目录

Lib 系统核心基类库目录

ThinkPHP/Core/Think.class.php //入口文件(设置异常和错误处理机制、注册系统自动加载机制、预编译当前项目、加载框架惯例配置文件)

ThinkPHP/Lib/Core/Log.class.php,    // 日志处理类

ThinkPHP/Lib/Core/Dispatcher.class.php, // URL调度类

ThinkPHP/Lib/Core/App.class.php,   // 应用程序类

ThinkPHP/Lib/Core/Action.class.php, // 控制器类

ThinkPHP/Lib/Core/View.class.php,  // 视图类

Tpl 系统模板目录

ThinkPHP/Tpl/default_index.tpl //测试模块的模板

ThinkPHP/Tpl/dispatch_jump.tpl //页面提示的模板

ThinkPHP/Tpl/page_trace.tpl //页面Trace信息的模板

ThinkPHP/Tpl/think_exception.tpl //系统错误信息的模板

Extend 框架扩展目录

<?php

/*

* 1、用户访问网站URL地址

* /test/Index.php 项目入口文件

*/

require (‘../ThinkPHP/ThinkPHP.php‘);//引入框架文件

/*

* 2、载入系统入口文件ThinkPHP.php

* ThinkPHP/ThinkPHP.php框架入口文件

* 判断系统常量,如果没有定义则自动生成

*/

$GLOBALS[‘_beginTime‘] = microtime(TRUE); //记录运行时的时间

define(‘MEMORY_LIMIT_ON‘,function_exists(‘memory_get_usage‘));//php中用echo memory_get_usage() 获取当前的内存消耗量

if(MEMORY_LIMIT_ON) $GLOBALS[‘_startUseMems‘] = memory_get_usage();//如果支持内存使用量函数则全局变量$GLOBALS[‘_startUseMems‘]等于内存使用量,这里相当于记录下内存初始使用的量

defined(‘APP_PATH‘) or define(‘APP_PATH‘, dirname($_SERVER[‘SCRIPT_FILENAME‘]).‘/‘);//设置当前的项目路径

defined(‘RUNTIME_PATH‘) or define(‘RUNTIME_PATH‘,APP_PATH.‘Runtime/‘);//设置runtime的路径

defined(‘APP_DEBUG‘) or define(‘APP_DEBUG‘,false); // 是否调试模式,默认false

$runtime = defined(‘MODE_NAME‘)?‘~‘.strtolower(MODE_NAME).‘_runtime.php‘:‘~runtime.php‘;//如果设置了其它运行模式则~ruturn.php就变化为 ~模式ruturn.php,(例如,采用命令行模式运行),那么在入口文件中定义MODE_NAME如下define(‘MODE_NAME‘,‘cli‘);  那么这里的缓存文件就为~cliruturn.php

defined(‘RUNTIME_FILE‘) or define(‘RUNTIME_FILE‘,RUNTIME_PATH.$runtime);//设置~runtime.php的路径;

if(!APP_DEBUG && is_file(RUNTIME_FILE)) {

// 部署模式直接载入运行缓存

require RUNTIME_FILE;

}else{//高度模式下加载框架下的Common/runtime.php文件;我们就模拟程序第一次运行,从下面开始进入

// 系统目录定义

defined(‘THINK_PATH‘) or define(‘THINK_PATH‘, dirname(__FILE__).‘/‘);

// 加载运行时文件,载入系统运行时文件runtime.php并定义项目路径常量

require THINK_PATH.‘Common/runtime.php‘;

}

/*

* 3、载入系统运行时文件runtime.php并定义项目路径常量

* ThinkPHP/Common/runtime.php 系统运行时文件

* ThinkPHP 运行时文件 编译后不再加

*/

//从18——81行定义了程序中要用到的各种常量

//#84行  为了方便导入第三方类库 设置Vendor目录到include_path

set_include_path(get_include_path() . PATH_SEPARATOR . VENDOR_PATH);//这一句将/ThinkPHP/Extend/Vendor/ 加入了include_path目录中

//#87-114行 加载运行时所需要的文件 并负责自动目录生成

function load_runtime_file() {

//引入/ThinkPHP/Lib/Core/Think.class.php

//引入/ThinkPHP/Lib/Core/ThinkException.class.php,  // 异常处理类

//引入/ThinkPHP/Lib/Core/Behavior.class.php,

// 检查项目目录结构 如果不存在则自动创建   build_app_dir();check_runtime();

}

//#118-130行 // 检查缓存目录(Runtime) 如果不存在则自动创建

function check_runtime() {

// 在load_runtime_file()中被引用

}

//#133-163行 //创建编译缓存

function build_runtime_cache($append=‘‘) {

//没仔细研究,是把程序中用到的代码都压入到~return.php缓存文件中

}

//#164-174 //// 编译系统行为扩展类库

function build_tags_cache() {

//编译系统行为扩展类库 compile($filename)此函数

}

//117-224 // 创建项目目录结构

function build_app_dir(){

//创建项目目录结构

}

//227-230 // 创建测试Action

function build_first_action(){

// /ThinkPHP/Tpl/default_index.tpl模板

}

//#233 加载运行时所需文件

load_runtime_file();

//#235 记录加载文件时间

G(‘loadTime‘);

// 执行入口

Think::Start();

/*

* 执行入口

* /ThinkPHP/Lib/Core/Think.class.php

* 程序的入口(设置异常和错误处理机制、注册系统自动加载机制、预编译当前项目、加载框架惯例配置文件)

*/

//#37-49 应用程序初始化

static public function Start() {

// 设定错误和异常处理

set_error_handler(array(‘Think‘,‘appError‘)); //将this->appError()注册为错误处理方法

set_exception_handler(array(‘Think‘,‘appException‘)); //将$this->appException()注册为异常处理函数

// 注册AUTOLOAD方法

spl_autoload_register(array(‘Think‘, ‘autoload‘));//将$this->autoload()注册为__autoload()方法

//[RUNTIME]

Think::buildApp();         // 读取配置信息 预编译项目

//[/RUNTIME]

// 运行应用

App::run();

return ;

}

//#61-124 读取配置信息 预编译项目

static private function buildApp() {

// /ThinkPHP/Conf/convention.php 读取系统配置,读取项目配置 /项目/Conf/conf.php 合并配置

//加载语言包(可能是支持多语言的,目前没用到过)

//加载模式系统行为定义(没看明白)

//读取核心编译文件列表 (使用compile()函数,将所有核心类及函数、自定义类库压入到$compile变量中,等待编译~runtime.php)

(

ThinkPHP/Common/functions.php, // 标准模式函数库

ThinkPHP/Lib/Core/Log.class.php,    // 日志处理类

ThinkPHP/Lib/Core/Dispatcher.class.php, // URL调度类

ThinkPHP/Lib/Core/App.class.php,   // 应用程序类

ThinkPHP/Lib/Core/Action.class.php, // 控制器类

ThinkPHP/Lib/Core/View.class.php,  // 视图类

);

// ThinkPHP/Common/common.php //加载项目公共文件(函数库)

// 项目/Conf/alias.php 加载项目别名定义(项目自定义类库,用别名导入的方式引入的自定义类库)

// 如果是调试模式,加载系统默认的配置文件ThinkPHP/Conf/debug.php的默认调试配置,如果项目自己定义了 项目/Conf/debug.php则读取项目调试配置

// 最后如果是部署模式 使用build_runtime_cache($compile); 创建编译缓存,将$compile变量写入到项目/Runtime/~runtime.php文件中

// 欧克至此该引入的文件全部引入完成,下面讲解控制器篇

// 最后的最后我们来回顾一下我们加载了些什么东西到程序中来

/*

* 1、ThinkPHP/Common/functions.php, // 标准模式函数库

* 2、ThinkPHP/Lib/Core/Log.class.php,    // 日志处理类

* 3、ThinkPHP/Lib/Core/Dispatcher.class.php, // URL调度类

* 4、ThinkPHP/Lib/Core/App.class.php,   // 应用程序类

* 5、ThinkPHP/Lib/Core/Action.class.php, // 控制器类

* 6、ThinkPHP/Lib/Core/View.class.php,  // 视图类

* 7、ThinkPHP/Common/common.php //加载项目公共文件(函数库)

* 8、项目/Conf/alias.php 加载项目别名定义(项目自定义类库,用别名导入的方式引入的自定义类库)

* 9、ThinkPHP/Conf/debug.php的默认调试配置

*/

}

/*

* 加载了半天文件再在来真的了,正式进入程序运行 App::run();

* /ThinkPHP/lib/Core/App.class.php //应用程序类 执行应用过程管理

* 这里是整个程序的入口,我们要跳着读,首先跳入App::init();

* 最后一步跳入App::exec();

*/

// #148-164 运行应用实例 入口文件使用的快捷方法

static public function run() {

// 项目初始化标签

tag(‘app_init‘);

App::init();

// 项目开始标签

tag(‘app_begin‘);

// Session初始化

session(C(‘SESSION_OPTIONS‘));

G(‘initTime‘);

//执行应用程序

App::exec();

// 项目结束标签

tag(‘app_end‘);

// 保存日志记录

if(C(‘LOG_RECORD‘)) Log::save();

return ;

}

// #36-76 应用程序初始化

static public function init() {

// load_ext_file(); 加载动态项目公共文件和配置

// URL调度 Dispatcher::dispatch(); //进入Thinkphp/lib/Core/Dispatcher.class.php dispatch()方法,进行URL参数常量赋值

// 如果有分组,include 分组配置与函数文件

// 获取模板主题名称 $templateSet = C(‘DEFAULT_THEME‘);

// 定义模板路径常量 define(‘THEME_PATH‘,   TMPL_PATH.$group.(THEME_NAME?THEME_NAME.‘/‘:‘‘));

// 定义模板路径常量 define(‘APP_TMPL_PATH‘,__ROOT__.‘/‘.APP_NAME.(APP_NAME?‘/‘:‘‘).basename(TMPL_PATH).‘/‘.$group.(THEME_NAME?THEME_NAME.‘/‘:‘‘));

// 定义模板文件的位置 C(‘TEMPLATE_NAME‘,THEME_PATH.MODULE_NAME.(defined(‘GROUP_NAME‘)?C(‘TMPL_FILE_DEPR‘):‘/‘).ACTION_NAME.C(‘TMPL_TEMPLATE_SUFFIX‘));

// 定义模板文件缓存的位置 C(‘CACHE_PATH‘,CACHE_PATH.$group);

}

// #89-138 执行应用程序

static public function exec() {

// 安全检测,模块名必须是英文和数字组成,且英文为首

// 检测通过 实例化 控制器 $module  =  A($group.MODULE_NAME);

// 检测如果定义了__hack_module()扩展,则遇到当前模块不存在时会被接管,优先级大于空模块EmptyAction

// 检测如果否定义空模块EmptyAction,则遇到不存在模块时调用EmptyAction模块

// 检测前置后置方法(_before_、_after_) method_exists($module,‘_before_‘.$action),如果有则执行 call_user_func(array(&$module,‘_before_‘.$action));

// 亮点在这里,至此URL解析完成了,跳转到 某某模块下的某某方法 执行当前操作 call_user_func(array(&$module,$action));

}

/*

* 接上一步,跳入App::init();后,我们发现需要跳入Dispatcher::dispatch();URL调度类中进行URL参数常量赋值,下面带你进入Disppatcher::dispatch方法中进行常量赋值

* ThinkPHP内置的Dispatcher类 只提供了一个公共的静态方法dispatch()方法作为接口,本方法将路由变量压入到常量中,供后面的action类使用

* 完成URL解析、路由和调度

* ThinkPHP/lib/Core/Dispatcher.class.php

* 完成后跳回到App::init();

*/

//#36-153 URL映射到控制器

static public function dispatch() {

//C(‘URL_MODEL‘);读取URL模式

//将模块提取到MODULE_NAME全局变量中,将动作提取到ACTION_NAME全局变量中,将参数

// $paths = explode($depr,trim($_SERVER[‘PATH_INFO‘],‘/‘));//解析path_info

//$var[C(‘VAR_MODULE‘)]  =   array_shift($paths);//提取模块

// $var[C(‘VAR_ACTION‘)]  =   array_shift($paths);//提取方法

// $_GET   =  array_merge($var,$_GET);//重写$_GET

// pathinfo常量 __INFO__

// 控制器常量 define(‘MODULE_NAME‘,self::getModule(C(‘VAR_MODULE‘)));

// 方法常量 define(‘ACTION_NAME‘,self::getAction(C(‘VAR_ACTION‘)));

// URL常量 define(‘__SELF__‘,strip_tags($_SERVER[‘REQUEST_URI‘]));

// 当前项目地址常量 define(‘__APP__‘,strip_tags(PHP_FILE));

// 当前操作地址常量 define(‘__ACTION__‘,__URL__.$depr.ACTION_NAME);

// $_REQUEST = array_merge($_POST,$_GET); $_REQUEST重写

}

//注:获取系统配置用C()方法,此方法写得很巧妙,建议大家看一下

时间: 2024-10-05 16:53:30

Thinkphp 源码分析的相关文章

thinkphp 源码分析(一)

在工作中,我接触到了国人开发的thinkphp框架,也许对于大多数童鞋来说,就是简单的用用,但是作为一名技术的爱好者,还是不放弃一丝希望,认真的开始读thinkphp的源码,但是对于很多的牛牛们来说,很简单,so easy!我是菜鸟,我需要的更多的学习的地方.作为以后的复习的笔记,保存一份,我平时太懒,一般不想去写博客:如果有php或者架构的同仁.欢迎加入我们的群:13768387 首先,我说一下,thinkphp的版本,我认为一个经典的版本是3.1.3,但是目前工作是这个经典版本和3.2版本,

Thinkphp源码分析系列(一)–入口文件

正如官方文档上所介绍的,thinkphp使用单一入口,所有的请求都从默认的index.php文件进入.当然不是说一定非得从index.php进入,这应该取决于你的服务器配置,一般服务器都会有默认的首页,比如index.php,index.html,所以一般访问域名都会先默认访问上述文件,你还可以创建多个应用,一个应用对应一个入口文件,所有的入口文件都引用一套thinkphp类库. 我们来看index.php都干了些什么.  index.php主要任务是定义应用名称和引用类库路径.当然也可以定义一

ThinkPHP 源码分析之常量 IS_AJAX

在控制器中判断请求是否是通过 AJAX 提交,ThinkPHP(3.2.2)中在 ThinkPHP/Library/Think/App.class.php (Line:49) 中定义了常量 IS_AJAX: define('IS_AJAX', ((isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') || !empty($_

TeamTalk源码分析之login_server

login_server是TeamTalk的登录服务器,负责分配一个负载较小的MsgServer给客户端使用,按照新版TeamTalk完整部署教程来配置的话,login_server的服务端口就是8080,客户端登录服务器地址配置如下(这里是win版本客户端): 1.login_server启动流程 login_server的启动是从login_server.cpp中的main函数开始的,login_server.cpp所在工程路径为server\src\login_server.下表是logi

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A

HashMap与TreeMap源码分析

1. 引言     在红黑树--算法导论(15)中学习了红黑树的原理.本来打算自己来试着实现一下,然而在看了JDK(1.8.0)TreeMap的源码后恍然发现原来它就是利用红黑树实现的(很惭愧学了Java这么久,也写过一些小项目,也使用过TreeMap无数次,但到现在才明白它的实现原理).因此本着"不要重复造轮子"的思想,就用这篇博客来记录分析TreeMap源码的过程,也顺便瞅一瞅HashMap. 2. 继承结构 (1) 继承结构 下面是HashMap与TreeMap的继承结构: pu

Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7)【转】

原文地址:Linux内核源码分析--内核启动之(5)Image内核启动(rest_init函数)(Linux-3.0 ARMv7) 作者:tekkamanninja 转自:http://blog.chinaunix.net/uid-25909619-id-4938395.html 前面粗略分析start_kernel函数,此函数中基本上是对内存管理和各子系统的数据结构初始化.在内核初始化函数start_kernel执行到最后,就是调用rest_init函数,这个函数的主要使命就是创建并启动内核线

Spark的Master和Worker集群启动的源码分析

基于spark1.3.1的源码进行分析 spark master启动源码分析 1.在start-master.sh调用master的main方法,main方法调用 def main(argStrings: Array[String]) { SignalLogger.register(log) val conf = new SparkConf val args = new MasterArguments(argStrings, conf) val (actorSystem, _, _, _) =

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三)

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及PeerSync策略.本文以及后续的文章将重点介绍Replication策略.Replication策略不但可以在SolrCloud中起到leader到replica的数据同步,也可以在用多个单独的Solr来实现主从同步.本文先介绍在SolrCloud的leader到replica的数据同步,下一篇