Stay hungry, Stay foolish

0%

TP笔记4:App(一)

runtime.php里的程序跑完后就加到了ThinkPHP.php里,ThinkPHP.php跑完后就加到了入口文件index.php里了。接下来印到眼前的是:

1
App::run();

App就是框架的Core目录下的App.class.php,由上一节可以知道,在编译~runtime.php文件时它就被加载并编译到缓存里了。这个App可以理解为应用启动器。它有哪些东西呢?

  • init、build、exec、run这几个方法构成了这个“程序”的执行流程

  • getGroup、getModule、getAction获取分组名、模块名、操作名

  • appError、appException 自定义的错误处理和异常处理

  • checkLanguage、checkTemplate 语言检查、模板检查

程序是从run方法开始的,那就从它入手吧:

1
2
3
4
5
6
7
8
9
static public function run() {
App::init();
// 记录应用初始化时间
if(C('SHOW_RUN_TIME')) $GLOBALS['_initTime'] = microtime(TRUE);
App::exec();
// 保存日志记录
if(C('LOG_RECORD')) Log::save();
return ;
}

它只是对init和exec的一个封装……而实际的启动是从init方法开始的:

  • 定义错误和异常处理,把它们交给本类的appError和appException

  • 编译项目缓存~app.php (在build方法里处理)

  • 设置系统时区,注册autoload方法

  • URL路由 (这是框架里的重量级的东西,之后会单开一篇对它进行分析)

  • 加载分组配置文件及模块配置文件(如果有的话)

  • 语言检查、模板检查、开始静态缓存(取决于配置项HTML_CACHE_ON)

自定义错误和异常处理

1
2
set_error_handler(array('App','appError'));
set_exception_handler(array('App','appException'));

把错误处理和异常处理交给App类里的appError方法和 appException。(这方面的内容可以参见:php错误处理

随便看一例:

1
2
3
4
5
case E_USER_ERROR:
$errorStr = "[$errno] $errstr ".basename($errfile)." 第 $errline 行.";
if(C('LOG_RECORD')) Log::write($errorStr,Log::ERR);
halt($errorStr);
break;

E_USER_ERROR可以用php函数trigger_error来发出:

1
trigger_error("这一条是测试信息", E_USER_ERROR);

显示结果:

继续看init方法里的内容:

1
2
3
4
5
6
7
8
9
if(defined('RUNTIME_MODEL')){
// 运行模式无需载入项目编译缓存
}elseif(is_file(RUNTIME_PATH.'~app.php') && (!is_file(CONFIG_PATH.'config.php') || filemtime(RUNTIME_PATH.'~app.php')>filemtime(CONFIG_PATH.'config.php'))) {
// 直接读取编译后的项目文件
C(include RUNTIME_PATH.'~app.php');
}else{
// 预编译项目
App::build();
}

在这里见到了一位老朋友——RUNTIME_MODEL,如果是allinone模式的话,会定义这个常量。在这里剩下的部分也就省了。

如果不是的话,就要对~app.php进行判断:首先判断这个文件和项目配置文件config.php是否存在,之前检测缓存文件是否是最新的——通过filetime函数查询文件的时间来判断。然后根据判断的结果来进入相应的分支:直接读取缓存or重新生成缓存?

(这里先把init的流程走一遍,对于build方法的内容,放在下文解析)

1
2
3
4
5
6
if(C('APP_PLUGIN_ON'))   tag('app_begin');
if(function_exists('date_default_timezone_set'))
date_default_timezone_set(C('DEFAULT_TIMEZONE'));
if(C('APP_AUTOLOAD_REG') && function_exists('spl_autoload_register'))
spl_autoload_register(array('Think', 'autoload'));
if(C('SESSION_AUTO_START')) session_start();

分别根据配置来设置项目的开始标签、默认的时区、自动加载以及开启session。

1
if(C('URL_DISPATCH_ON'))   Dispatcher::dispatch();

如果配置里的URL路由打开了,就会执行URL路由。

1
2
if(!defined('PHP_FILE'))
define('PHP_FILE',_PHP_FILE_);

对常量PHP_FILE的检查。

1
2
3
4
5
6
7
8
9
if(C('APP_GROUP_LIST')) {
if(!defined('GROUP_NAME')) define('GROUP_NAME', App::getGroup()); // Group名称
// 分组配置文件
if(is_file(CONFIG_PATH.GROUP_NAME.'/config.php'))
C(include CONFIG_PATH.GROUP_NAME.'/config.php');
// 分组函数文件
if(is_file(COMMON_PATH.GROUP_NAME.'/function.php'))
include COMMON_PATH.GROUP_NAME.'/function.php';
}

加载分组配置文件和分组函数文件。我们可以专门针对分组来写配置文件和函数文件。格式分别是:

  • 项目目录/Conf(配置目录)/分组名/config.php

  • 项目目录/Common/分组名/config.php

1
2
if(!defined('MODULE_NAME')) define('MODULE_NAME',   App::getModule());       // Module名称
if(!defined('ACTION_NAME')) define('ACTION_NAME', App::getAction()); // Action操作

获取模块名和操作名。(在URL路由里处理,在exec方法里调用)

1
2
if(is_file(CONFIG_PATH.strtolower(MODULE_NAME).'_config.php'))
C(include CONFIG_PATH.strtolower(MODULE_NAME).'_config.php');

还可以为模块定义专用的配置文件,在此检测并加载。

1
2
3
4
5
6
7
App::checkLanguage();     //语言检查
App::checkTemplate(); //模板检查
if(C('HTML_CACHE_ON')) // 开启静态缓存
HtmlCache::readHTMLCache();

// 项目初始化标签
if(C('APP_PLUGIN_ON')) tag('app_init');
  • 检测浏览器语言,加载语言包

  • 检测模板主题

  • 检测是否开启静态缓存

  • 打开个标签。标签的介绍可以看2.1完全手册的6.2节《应用扩展》,这个功能在3.0里删除了。

init初始化完成,在进入exec之前,下节先把init里涉及到的一些方法和URL路由给通一通。^_^

据说打赏我的人,代码没有BUG