ThinkPHP 3.x Common/common.php 核心函数源码解析
ThinkPHP的常见功能封装于Common/common.php中。本文拆解该文件中配置管理(C)、标签扩展(tag)及行为执行(B)三大核心函数的源码实现,结合自动加载机制(aotload),还原框架运行时从入口到类库调用的完整链路。
配置参数管理函数 C()
C 函数是 ThinkPHP 获取和设置配置参数的入口,支持批量定义与缓存加载。其内部维护了一个静态数组 $_config 来存储当前请求的配置数据。
主要运行逻辑分为以下几种情况:
- 无参数或仅传入 $value: 尝试通过缓存键
'c_'.$value加载已保存的全局配置,合并后返回。此时会先使用array_change_key_case将配置键名统一转为小写,确保配置检索的一致性。 - 单字符串命名空间: 若
$name不包含点号(.),视为一级配置项。直接将其转为小写进行读写:若无$value则返回旧值;否则写入$_config。 - 二维配置项: 若
$name包含点号(如'DB.host'),按点号拆分。读写操作分别针对$_config[一级键][二级键]进行。 - 批量赋值: 当
$name为数组时,遍历所有键值对覆盖现有配置。若传入$value,则将合并后的结果存入缓存,供后续调用。
/**
* 获取和设置配置参数 支持批量定义
* @param string|array $name 配置变量
* @param mixed $value 配置值
* @return mixed
*/
function C($name=null, $value=null) {
static $_config = array();
// 无参数时获取所有
if (empty($name)) {
if(!empty($value) && $array = cache('c_'.$value)) {
//array_change_key_case():将数组中的所有键名改为全小写或大写,默认会改为全小写
$_config = array_merge($_config, array_change_key_case($array));
}
return $_config;
}
// 优先执行设置获取或赋值,如果参数2为空,则为获取配置配置变量的值的情况;如果参数2不为空,则是为配置变量设置值的情况
if (is_string($name)) {//给配置变量设置值或者获取配置变量的值
if (!strpos($name, '.')) {//如果配置变量名中没有".",则执行下面代码
$name = strtolower($name);//将配置变量名转化为小写
if (is_null($value))
return isset($_config[$name]) ? $_config[$name] : null;//获取配置变量值的情况,返回配置变量的值
$_config[$name] = $value;//给配置变量设置值的情况
return;
}
// 二维数组设置和获取支持
$name = explode('.', $name);
$name[0] = strtolower($name[0]);
if (is_null($value))
return isset($_config[$name[0]][$name[1]]) ? $_config[$name[0]][$name[1]] : null;//获取值的情况
$_config[$name[0]][$name[1]] = $value;//赋值的情况
return;
}
// 批量设置
if (is_array($name)){//批量设置值
//array_change_key_case():将数组中的所有键名改为全小写或大写,默认会改为全小写,注意:只是将键名改为小写哦
$_config = array_merge($_config, array_change_key_case($name));
if(!empty($value)) { // 保存配置值
cache('c_'.$value,$_config);
}
return;
}
return null; // 避免非法参数
}
标签扩展系统 tag() 与行为执行 B()
ThinkPHP 利用“标签位(Tag)”与“行为(Behavior)”实现钩子功能。tag()负责定位钩子并触发一系列行为,B()负责实例化并执行具体的行为类。
第1步:调用标签钩子 tag()
通过 tag('app_begin') 等形式触发。函数首先从配置中读取系统级扩展(extends)和项目级扩展(tags)。若两者同时存在且未设置覆盖,则进行合并去重。随后进入循环:
- 判断是否为自定义路径扩展,提取完整类名。
- 调用
B($name, $params)执行具体行为。 - 在调试模式下记录行为的开始与结束时间日志。
/**
* 处理标签扩展,如: 项目开始标签的调用,tag('app_begin');
* @param string $tag 标签名称
* @param mixed $params 传入参数
* @return mixed
*/
function tag($tag, &$params=NULL) {
// 系统或框架标签扩展
// C('extends', include THINK_PATH.'Conf/tags.php');//这个文件很重要
$extends = C('extends.' . $tag);//$_config['extends'][app_init]
// 应用或项目标签扩展
// C('tags', include CONF_PATH.'tags.php'); //项目中也可以编写自己的tags.php文件
$tags = C('tags.' . $tag);//$_config['tags'][app_init]
if (!empty($tags)) {//如果$tags不为空
if(empty($tags['_overlay']) && !empty($extends)) { // 合并扩展
$tags = array_unique(array_merge($extends,$tags));
}elseif(isset($tags['_overlay'])){ // 通过设置 '_overlay'=>1 覆盖系统标签
unset($tags['_overlay']);
}
}elseif(!empty($extends)) {
$tags = $extends;
}
if($tags) {
if(APP_DEBUG) {
G($tag.'Start');
trace('[ '.$tag.' ] --START--','','INFO');
}
// 执行扩展
foreach ($tags as $key=>$name) {
if(!is_int($key)) { // 指定行为类的完整路径 用于模式扩展
$name = $key;
}
//创建行为类对象,如:$class=$name.'Behavior';$behavior = new $class();//创建一个行为类对象
B($name, $params);//执行某个行为,参数1-行为名称 参数2-传入的参数
}
if(APP_DEBUG) { // 记录行为的执行日志
trace('[ '.$tag.' ] --END-- [ RunTime:'.G($tag.'Start',$tag.'End',6).'s ]','','INFO');
}
}else{ // 未执行任何行为 返回false
return false;
}
}
第2步:实例化并运行行为 B()
B 函数根据传入的名称拼接出行为类文件名(后缀默认为 Behavior)。随后使用框架的 G() 计时器记录耗时,实例化该类对象并调用其 run 方法传入参数。最后同样在调试模式下输出行为运行日志。
/**
* 执行某个行为
* @param string $name 行为名称
* @param Mixed $params 传人的参数
* @return void
*/
function B($name, &$params=NULL) {
//行为类在Think.class.php文件中的autoload($class)函数中自动加载
$class = $name.'Behavior';//行为类的名称
G('behaviorStart');
$behavior = new $class();//创建一个行为类对象
$behavior->run($params);//给当前行为类对象传递参数
if(APP_DEBUG) { // 记录行为的执行日志
trace('Run '.$name.' Behavior [ RunTime:'.G('behaviorStart','behaviorEnd',6).'s ]','','INFO');
}
}
类库自动加载机制 autoload()
框架启动后,通过注册 spl_autoload_register 指向 Think::autoload,实现了无需手动 include 即可加载类的能力。
autoload 的执行优先级如下:
- 别名检查: 调用
alias_import检查是否已有预定义的别名映射,若有则直接导入并返回。 - 行为类匹配: 若类名以
Behavior结尾,依次尝试从 Core、Extend 目录及当前模式(Mode)的 Library 下加载。 - 模型类匹配: 若类名以
Model结尾,优先加载分目录(Model/Group/)下的模型,再尝试基础目录与扩展目录。 - 控制器类匹配: 若类名以
Action结尾,优先加载分目录(Action/Group/)下的控制器,再尝试基础目录与扩展目录。 - 自定义路径搜索: 解析配置项
APP_AUTOLOAD_PATH,在指定的逗号分隔路径列表中查找并引入类文件。
此机制确保了框架内核心组件的高性能载入,同时也允许开发者通过配置扩展第三方库的加载位置。
/**
* 系统自动加载ThinkPHP类库
* 并且支持配置自动加载路径
* @param string $class 对象类名
* @return void
*/
//行为类、模型类、控制器类 都是通过autoload函数自动加载的
public static function autoload($class) {
// 检查是否存在别名定义
if(alias_import($class)) return ;
//行为类都是通过此函数自动加载的
if(substr($class,-8)=='Behavior') { // 加载行为
if(require_cache(CORE_PATH.'Behavior/'.$class.'.class.php')
|| require_cache(EXTEND_PATH.'Behavior/'.$class.'.class.php')
|| require_cache(LIB_PATH.'Behavior/'.$class.'.class.php')
|| (defined('MODE_NAME') && require_cache(MODE_PATH.ucwords(MODE_NAME).'/Behavior/'.$class.'.class.php'))) {
return ;
}
}elseif(substr($class,-5)=='Model'){ // 加载模型
if((defined('GROUP_NAME') && require_cache(LIB_PATH.'Model/'.GROUP_NAME.'/'.$class.'.class.php'))
|| require_cache(LIB_PATH.'Model/'.$class.'.class.php')
|| require_cache(EXTEND_PATH.'Model/'.$class.'.class.php') ) {
return ;
}
}elseif(substr($class,-6)=='Action'){ // 加载控制器
if((defined('GROUP_NAME') && require_cache(LIB_PATH.'Action/'.GROUP_NAME.'/'.$class.'.class.php'))
|| require_cache(LIB_PATH.'Action/'.$class.'.class.php')
|| require_cache(EXTEND_PATH.'Action/'.$class.'.class.php') ) {
return ;
}
}
// 根据自动加载路径设置进行尝试搜索
$paths = explode(',',C('APP_AUTOLOAD_PATH'));
foreach ($paths as $path){
if(import($path.'.'.$class))
// 如果加载类成功则返回
return ;
}
}
总结
理解 C、tag、B 与 autoload 这四个函数的交互逻辑,是掌握 ThinkPHP 3.x 架构运作的关键。它们共同构成了框架的基础设施层,支撑起从配置读取到动态路由、再到插件扩展的整个生命周期。
以上就是ThinkPHP中Common/common.php文件常用函数功能的详细内容,更多关于ThinkPHP框架原理的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













