本系列文章基于 ThinkPHP 3.2.3 版本。
tp 框架是单入口,一般是先从 index.php 开始。在 index.php 中,开发者可以在引入 ThinkPHP 公共入口文件前,自定义所需的项目常量和进行一些准备工作。
ThinkPHP 公共入口文件,即 ThinkPHP.php。在这个 php 文件中,tp 框架定义了一些默认的系统常量,最后调用 Think\Think::start() 来初始化应用,在调用该方法前应先在加载该核心 Think 类。
前面的定义各种常量的过程没什么好讲的,本文主要围绕 Think\Think::start() 这个初始化方法来讲。在这个方法中,它首先注册了类的自动加载方法——Think\Think::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 类,所以在调用该类时使用了注册的自动加载方法,在 autoload 方法自动把 Storage 类加载进来。这里调用 Storage 类主要用于在第一次运行时将核心文件等文件缓存,不过这里还有一个判断条件,如果开发者将 APP_DEBUG 定义为了 true,即开启调试模式,则不会进行缓存。该缓存文件的路径为 RUNTIME_PATH.APP_MODE.'~runtime.php'。
加载配置文件、别名定义、应用行为定义这三种文件时都分为了两步,第一步是先加载框架默认的相应文件,第二步是加载开发者定义的相应文件。框架的默认文件内容会被开发者定义的文件内容覆盖掉。
// 初始化文件存储方式
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);
}
}
紧接着读取当前应用状态对应的配置文件,开发者可以根据不同的应用状态设置不同的配置文件。
设置系统时区后,开始检查应用目录结构,如果不存在则自动创建。这一步使用到了 Bulid 类,它会自动帮开发者在应用目录下按照模块名创建相应的 Controller、View、Model、Conf等文件夹以及文件夹下的一些初始 php 文件。
加载完所有的文件之后,记录加载文件时间,然后调用 App::run() 方法。