您的位置:首页 > 编程语言 > PHP开发

thinkPHP 框架 引导类Think.class.php 分析(一)

2016-10-27 11:48 471 查看
thinkPHP框架中的引导类(核心类)Think.class.php是一个非常重要的类。该类主要包含如下属性和方法:



该类主要在公告入口文件ThinkPHP.php中的最后一行被引用。

// 加载核心Think类
require CORE_PATH.'Think'.EXT;
// 应用初始化
Think\Think::start();

所以我们从strat方法开始分析。

static public function start() {
// 注册AUTOLOAD方法
spl_autoload_register('Think\Think::autoload');
// 设定错误和异常处理
register_shutdown_function('Think\Think::fatalError');
set_error_handler('Think\Think::appError');
set_exception_handler('Think\Think::appException');
// 初始化文件存储方式
Storage::connect(STORAGE_TYPE);
$runtimefile  = RUNTIME_PATH.APP_MODE.'~runtime.php';
if(!APP_DEBUG && Storage::has($runtimefile)){
Storage::load($runtimefile);
}else{
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  .=  "\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'.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'));

// 检查应用目录结构 如果不存在则自动创建
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::run();
}


spl_autoload_register 

将函数注册到SPL __autoload函数队列中。如果该队列中的函数尚未激活,则激活它们。

如果在你的程序中已经实现了__autoload()函数,它必须显式注册到__autoload()队列中。因为 spl_autoload_register()函数会将Zend
Engine中的__autoload()函数取代为spl_autoload()spl_autoload_call()

如果需要多条 autoload 函数,spl_autoload_register() 满足了此类需求。 它实际上创建了 autoload 函数的队列,按定义时的顺序逐个执行。相比之下, __autoload() 只可以定义一次。
所以我们只需要看autoload()函数。下面register_shutdown_function和set_error_handler、set_exception_handler 都只需要看里里面的函数即可。autoload函数实现了类库的自动加载,下面我详细的分析下autoload函数。

public static function autoload($class)
{
// 检查是否存在映射
//首先在加载未知的类时,根据类名检测在静态变量$_map中是否存在这样的类,存在的话,就不用再次加载了
if (isset(self::$_map[$class])) {
include self::$_map[$class];

//判断在实例化类时是否存在反斜杠,不存在的话,直接。。。。。
} elseif (false !== strpos($class, '\\')) {
//strstr()函数 详解(因为本人在这个函数上浪费过时间,故而提醒广大朋友)
// $str="dsdfsd/mfg";
//strstr("/",$str);找到 / 在字符串第一次出现的位置,并返回剩余的部分。结果为  /mfg
//strstr("/",$str,true);找到 / 在字符串第一次出现的位置,并返回之前的部分  dsdfsd。
//所以这个true还是很关键的。
$name = strstr($class, '\\', true);
//如果返回的$name 在以下array('Think', 'Org', 'Behavior', 'Com', 'Vendor')中或者在ThinkPHP/Library 中会自动定位
// 再结合下面的代码 可以判断
//        new  Think\Page() (前提对应目录下存在这样的类库)
//        new  Vendor\Page()
//  像这样形式的实例化应该可以加载类库,但是在thinkphp中还是报错。 new  \Think\Page()  这样才会正常
// 当时我看到这段代码感觉  new  Think\Page() 符合要求啊,于是我百思不得其姐。最后将问题定位到命名空间请看
//下面的命名空间讲解。我看完就明白了。
//最后我得出结论:autoload($class) 中参数$class 是结合命名空间生成的
//new  Think\Page() 这样实例化的话,生成的$class 是带上命名空间的 如 Home\Controller\Think\Page
if (in_array($name, array('Think', 'Org', 'Behavior', 'Com', 'Vendor')) || is_dir(LIB_PATH . $name)) {
// Library目录下面的命名空间自动定位
$path = LIB_PATH;
} else {
// 检测自定义命名空间 否则就以模块为命名空间
$namespace = C('AUTOLOAD_NAMESPACE');
$path = isset($namespace[$name]) ? dirname($namespace[$name]) . '/' : APP_PATH;
}
$filename = $path . str_replace('\\', '/', $class) . EXT;
if (is_file($filename)) {
// Win环境下面严格区分大小写
if (IS_WIN && false === strpos(str_replace('/', '\\', realpath($filename)), $class . EXT)) {
return;
}
include $filename;
}
//关闭命名空间,可以自行探索
} elseif (!C('APP_USE_NAMESPACE')) {
// 自动加载的类库层
foreach (explode(',', C('APP_AUTOLOAD_LAYER')) as $layer) {
if (substr($class, -strlen($layer)) == $layer) {
if (require_cache(MODULE_PATH . $layer . '/' . $class . EXT)) {
return;
}
}
}
// 根据自动加载路径设置进行尝试搜索
foreach (explode(',', C('APP_AUTOLOAD_PATH')) as $path) {
if (import($path . '.' . $class))
// 如果加载类成功则返回
return;
}
}
}


这里使用了私有静态变量$_map,只能在类的内部访问,而不能在类的外部访问。静态成员只保留一个变量值,而这个变量值对所有的实例都是有效,也就是说,所有的实例共享这个成员。 这个变量中存放着已经加载过的类的名称。当然thinkphp初始化的时候,会自动加载一些类。至于如何自动加载,暂时留一个疑问?我们接着讲解autoload函数。

命名空间讲解:



时间有限,先写这些。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: