// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st
// +----------------------------------------------------------------------
namespace
think;
use think\exception\
ClassNotFoundException;
use think\exception\
HttpException;
use think\exception\
HttpResponseException;
use think\exception\
RouteNotFoundException;
/**
* App 应用管理
*
@author
liu21st
*/
class
App
{
/**
*
@var
bool
是否初始化过
*/
protected
static
$init =
false;
/**
*
@var
string
当前模块路径
*/
public
static
$modulePath;
/**
*
@var
bool
应用调试模式
*/
public
static
$debug =
true;
/**
*
@var
string
应用类库命名空间
*/
public
static
$namespace =
'app';
/**
*
@var
bool
应用类库后缀
*/
public
static
$suffix =
false;
/**
*
@var
bool
应用路由检测
*/
protected
static
$routeCheck;
/**
*
@var
bool
严格路由检测
*/
protected
static
$routeMust;
/**
*
@var
array
请求调度分发
*/
protected
static
$dispatch;
/**
*
@var
array
额外加载文件
*/
protected
static
$file = [];
/**
* 执行应用程序
*
@access
public
*
@param
Request
$request 请求对象
*
@return
Response
*
@throws
Exception
*/
//thinkphp5框架入口,执行run方法
public
static
function
run(
Request
$request =
null)
{
//返回实例think\Request方法,该方法在thinkphp\library\think\Request.php中
$request =
is_null(
$request) ?
Request::
instance() :
$request;
//捕获异常
try {
// 执行当前类的initCommon方法,返回 \thinkphp\convention.php里的全部配置信息
$config =
self::
initCommon();
// 模块/控制器绑定
if (
defined(
'BIND_MODULE')) {
BIND_MODULE &&
Route::
bind(BIND_MODULE);
// \thinkphp\convention.php配置里auto_bind_module参数设为true时执行,默认不执行
}
elseif (
$config[
'auto_bind_module']) {
// 入口自动绑定
$name =
pathinfo(
$request->
baseFile(), PATHINFO_FILENAME);
if (
$name &&
'index' !=
$name &&
is_dir(APP_PATH .
$name)) {
Route::
bind(
$name);
}
}
//thinkphp\convention.php配置里default_filter参数设为true时执行,默认不执行
$request->
filter(
$config[
'default_filter']);
// 默认语言
Lang::
range(
$config[
'default_lang']);
// 开启多语言机制 检测当前语言
$config[
'lang_switch_on'] &&
Lang::
detect();
$request->
langset(
Lang::
range());
// 加载系统语言包
Lang::
load([
THINK_PATH .
'lang' . DS .
$request->
langset() . EXT,
APP_PATH .
'lang' . DS .
$request->
langset() . EXT,
]);
// 监听 app_dispatch
Hook::
listen(
'app_dispatch',
self::
$dispatch);
// 获取应用调度信息
$dispatch =
self::
$dispatch;
// 未设置调度信息则进行 URL 路由检测
if (
empty(
$dispatch)) {
/*执行当前类的routeCheck方法,获取调度信息,如访问index模块下index控制器里的index方法,则
$dispatch = array(2) { ["type"]=> string(6) "module"
["module"]=> array(3) {
[0]=> string(5) "index" [1]=> string(5) "index" [2]=> string(5) "index" } }
*/
$dispatch =
self::
routeCheck(
$request,
$config);
}
// 记录当前调度信息 将获取的调度信息,即模块,控制器,方法名存入Request类的dispatch属性中
$request->
dispatch(
$dispatch);
// 记录路由和请求信息 ,调式模式,在\application\config.php 参数app_debug可配置
if (
self::
$debug) {
//将调度信息写入日志 ,执行\thinkphp\library\think\Log.php 中 Log类的record方法
Log::
record(
'[ ROUTE ] ' .
var_export(
$dispatch,
true),
'info');
/*将\thinkphp\library\think\Request.php Request类中header方法
获取的头部信息写入日志 */
Log::
record(
'[ HEADER ] ' .
var_export(
$request->
header(),
true),
'info');
//记录当前请求的参数
Log::
record(
'[ PARAM ] ' .
var_export(
$request->
param(),
true),
'info');
}
// 监听 app_begin
Hook::
listen(
'app_begin',
$dispatch);
// 请求缓存检查
$request->
cache(
$config[
'request_cache'],
$config[
'request_cache_expire'],
$config[
'request_cache_except']
);
//执行当前类的exec方法
$data =
self::
exec(
$dispatch,
$config);
}
catch (
HttpResponseException
$exception) {
$data =
$exception->
getResponse();
}
// 清空类的实例化
Loader::
clearInstance();
// 输出数据到客户端 判断$data是否为Response类的实例
if (
$data instanceof Response) {
$response =
$data;
}
elseif (!
is_null(
$data)) {
// 默认自动识别响应输出类型
$type =
$request->
isAjax() ?
Config::
get(
'default_ajax_return') :
Config::
get(
'default_return_type');
/* 这句代码是整个框架的核心,$data 是获得反射类的实例,假如访问的是index模块下的index控制器中index方法
$data = new app\index\controller\Index();$data = $data->index();
为什么tp框架要用反射来执行类的方法? 当前App.php的命名空间是think,而控制器的命名空间是app\index\controller
反射很好的解决了执行不同命名空间下的类,反射可以简单地理解为实例化类后执行类中的方法。
具体可以查看本类的 invokeFunction invokeMethod invokeClass三个方法。
将返回的反射实例$data,也就是 app\index\controller\Index下的index方法,
存入 \thinkphp\library\think\Response.php Response类中的$data属性
*/
$response =
Response::
create(
$data,
$type);
}
else {
$response =
Response::
create();
}
// 监听 app_end
Hook::
listen(
'app_end',
$response);
//返回返回的反射实例
return
$response;
}
/**
* 初始化应用,并返回配置信息
*
@access
public
*
@return
array
*/
public
static
function
initCommon()
{
// 当前类中的init变量默认为false,也就是空,执行以下语句
if (
empty(
self::
$init)) {
/*判断APP_NAMESPACE这个常量是否存在,默认APP_NAMESPACE不存在,无需执行此语句,如要配置,
可在thinkphp\base.php下加上 define('APP_NAMESPACE', 'application');*/
if (
defined(
'APP_NAMESPACE')) {
//若配置APP_NAMESPACE,则当前类的$namespace变量修改为所配置的值
self::
$namespace = APP_NAMESPACE;
}
/*self::$namespace当前类的namespace属性,APP_PATH: application目录路径,
执行先前载入的\thinkphp\library\think\Loader.php文件Loader类中addNamespace方法*/
Loader::
addNamespace(
self::
$namespace, APP_PATH);
// 初始化应用,执行当前类的init方法,加载配置文件
$config =
self::
init();
self::
$suffix =
$config[
'class_suffix'];
// 应用调试模式
self::
$debug =
Env::
get(
'app_debug',
Config::
get(
'app_debug'));
if (!
self::
$debug) {
//设置错误信息的类别, ini_set用来设置php.ini的值,在脚本运行时保持新的值,并在脚本结束时恢复
ini_set(
'display_errors',
'Off');
}
elseif (!IS_CLI) {
// 重新申请一块比较大的 buffer
if (
ob_get_level() >
0) {
$output =
ob_get_clean();
}
ob_start();
if (!
empty(
$output)) {
echo
$output;
}
}
if (!
empty(
$config[
'root_namespace'])) {
Loader::
addNamespace(
$config[
'root_namespace']);
}
// 加载额外文件
if (!
empty(
$config[
'extra_file_list'])) {
foreach (
$config[
'extra_file_list'] as
$file) {
$file =
strpos(
$file,
'.') ?
$file : APP_PATH .
$file . EXT;
if (
is_file(
$file) && !
isset(
self::
$file[
$file])) {
include
$file;
self::
$file[
$file] =
true;
}
}
}
// 设置系统时区
date_default_timezone_set(
$config[
'default_timezone']);
// 监听 app_init
Hook::
listen(
'app_init');
self::
$init =
true;
}
return
Config::
get();
}
/**
* 初始化应用或模块
*
@access
public
*
@param
string
$module 模块名
*
@return
array
*/
//此方法主要载入application和模块中配置文件的信息
private
static
function
init(
$module =
'')
{
// 定位模块目录
$module =
$module ?
$module . DS :
'';
/* \tpfive\public/../application/init.php
加载初始化文件 ,判断application目录下是否存在init.php */
if (
is_file(APP_PATH .
$module .
'init' . EXT)) {
include APP_PATH .
$module .
'init' . EXT;
//判断runtime目录下是否存在init.php
}
elseif (
is_file(RUNTIME_PATH .
$module .
'init' . EXT)) {
//载入init.php文件
include RUNTIME_PATH .
$module .
'init' . EXT;
}
else {
/* $module值为空时获取application目录下的config.php里全部配置信息,$module有值时,
则获取$module,也就是模块目录下config.php里的配置,所以配置写在模块里面的config.php也是可用的 */
$config =
Config::
load(CONF_PATH .
$module .
'config' . CONF_EXT);
// 读取数据库配置文件 application目录下的database.php
$filename = CONF_PATH .
$module .
'database' . CONF_EXT;
Config::
load(
$filename,
'database');
// 读取扩展配置文件 判断是否存在application目录下extra目录
if (
is_dir(CONF_PATH .
$module .
'extra')) {
$dir = CONF_PATH .
$module .
'extra';
$files =
scandir(
$dir);
foreach (
$files as
$file) {
if (
'.' .
pathinfo(
$file, PATHINFO_EXTENSION) === CONF_EXT) {
$filename =
$dir . DS .
$file;
//载入 \application\extra下配置文件
Config::
load(
$filename,
pathinfo(
$file, PATHINFO_FILENAME));
}
}
}
// 加载应用状态配置
if (
$config[
'app_status']) {
Config::
load(CONF_PATH .
$module .
$config[
'app_status'] . CONF_EXT);
}
/* 加载行为扩展文件 $module值为空时,判断application\tags.php
有值时判断$module也就是地址栏模块下tags.php是否存在*/
if (
is_file(CONF_PATH .
$module .
'tags' . EXT)) {
/*application目录下的tags.php,执行\thinkphp\library\think\Hook.php Hook类中的import方法
传入参数为\application\tags.php 和地址栏模块下tags.php中的全部配置信息,
把参入参数的配置信息赋值到Hook类的$tags静态属性中*/
Hook::
import(
include CONF_PATH .
$module .
'tags' . EXT);
}
// 加载公共文件
$path = APP_PATH .
$module;
//判断\application\common.php 或地址栏模块下common.php是否存在
if (
is_file(
$path .
'common' . EXT)) {
//载入\application\common.php 和地址栏模块下common.php中的配置信息
include
$path .
'common' . EXT;
}
// 加载当前模块语言包
if (
$module) {
Lang::
load(
$path .
'lang' . DS .
Request::
instance()->
langset() . EXT);
}
}
//返回所有配置信息
return
Config::
get();
}
/**
* 设置当前请求的调度信息
*
@access
public
*
@param
array
|
string
$dispatch 调度信息
*
@param
string
$type 调度类型
*
@return
void
*/
public
static
function
dispatch(
$dispatch,
$type =
'module')
{
self::
$dispatch = [
'type' =>
$type,
$type =>
$dispatch];
}
/**
* 执行函数或者闭包方法 支持参数调用
*
@access
public
*
@param
string
|
array
|\
Closure
$function 函数或者闭包
*
@param
array
$vars 变量
*
@return
mixed
*/
public
static
function
invokeFunction(
$function,
$vars = [])
{
$reflect =
new
\ReflectionFunction(
$function);
$args =
self::
bindParams(
$reflect,
$vars);
// 记录执行信息
self::
$debug &&
Log::
record(
'[ RUN ] ' .
$reflect->
__toString(),
'info');
return
$reflect->
invokeArgs(
$args);
}
/**
* 调用反射执行类的方法 支持参数绑定
*
@access
public
*
@param
string
|
array
$method 方法
*
@param
array
$vars 变量
*
@return
mixed
*/
/*反射类的方法 $method = array(2) {
[0]=> object(app\index\controller\Index)#5 (0) { } [1]=> string(5) "index" } */
public
static
function
invokeMethod(
$method,
$vars = [])
{
//首先判断传递的第一个参数是否为数组
if (
is_array(
$method)) {
$class =
is_object(
$method[
0]) ?
$method[
0] :
self::
invokeClass(
$method[
0]);
//实例化反射类中的方法
$reflect =
new
\ReflectionMethod(
$class,
$method[
1]);
}
else {
// 静态方法
$reflect =
new
\ReflectionMethod(
$method);
}
//执行当前类的bindParams方法
$args =
self::
bindParams(
$reflect,
$vars);
//将当前反射类的信息写入日志
self::
$debug &&
Log::
record(
'[ RUN ] ' .
$reflect->
class .
'->' .
$reflect->
name .
'[ ' .
$reflect->
getFileName() .
' ]',
'info');
//执行反射类中的方法,并返回
return
$reflect->
invokeArgs(
isset(
$class) ?
$class :
null,
$args);
}
/**
* 调用反射执行类的实例化 支持依赖注入
*
@access
public
*
@param
string
$class 类名
*
@param
array
$vars 变量
*
@return
mixed
*/
//反射操作 $class = ''app\index\controller\Index';
public
static
function
invokeClass(
$class,
$vars = [])
{
/* ReflectionClass函数是建立 $class 这个反射类,假设$class = 'app\index\controller\Index';
也就是实例化 new app\index\controller\Index; */
$reflect =
new
\ReflectionClass(
$class);
//getConstructor 获取类的构造函数
$constructor =
$reflect->
getConstructor();
//传入类的构造函数,执行本类的bindParams方法
$args =
$constructor ?
self::
bindParams(
$constructor,
$vars) : [];
//newInstanceArgs 创建一个类的新实例,给出的参数将传递到类的构造函数
return
$reflect->
newInstanceArgs(
$args);
}
/**
* 绑定参数
*
@access
private
*
@param
\ReflectionMethod
|
\ReflectionFunction
$reflect 反射类
*
@param
array
$vars 变量
*
@return
array
*/
/* $reflect 为实例化反射类中的方法 ,假设访问index模块下index控制器中的index方法,
$reflect = new app\index\controller\Index(); $reflect = $reflect->index(); */
private
static
function
bindParams(
$reflect,
$vars = [])
{
//自动获取请求变量
if (
empty(
$vars)) {
/* 'url_param_type在\application\config.php配置
// URL参数方式 0 按名称成对解析 1 按顺序解析
'url_param_type' => 0,默认$vars 获取路由规则或参数配置
*/
$vars =
Config::
get(
'url_param_type') ?
Request::
instance()->
route() :
Request::
instance()->
param();
}
$args = [];
//getNumberOfParameters 获取构造函数参数的个数
if (
$reflect->
getNumberOfParameters() >
0) {
// 判断数组类型 数字数组时按顺序绑定参数
reset(
$vars);
$type =
key(
$vars) ===
0 ?
1 :
0;
//getParameters 获取函数全部参数
foreach (
$reflect->
getParameters() as
$param) {
//循环遍历,作为参数传递给当前类的getParamValue方法
$args[] =
self::
getParamValue(
$param,
$vars,
$type);
}
}
//返回处理后的参数
return
$args;
}
/**
* 获取参数值
*
@access
private
*
@param
\ReflectionParameter
$param 参数
*
@param
array
$vars 变量
*
@param
string
$type 类别
*
@return
array
*/
private
static
function
getParamValue(
$param,
&
$vars,
$type)
{
$name =
$param->
getName();
$class =
$param->
getClass();
//执行反射类的方法
if (
$class) {
$className =
$class->
getName();
$bind =
Request::
instance()->
$name;
if (
$bind instanceof
$className) {
$result =
$bind;
}
else {
if (
method_exists(
$className,
'invoke')) {
$method =
new
\ReflectionMethod(
$className,
'invoke');
if (
$method->
isPublic() &&
$method->
isStatic()) {
return
$className::
invoke(
Request::
instance());
}
}
$result =
method_exists(
$className,
'instance') ?
$className::
instance() :
new
$className;
}
}
elseif (
1 ==
$type && !
empty(
$vars)) {
$result =
array_shift(
$vars);
}
elseif (
0 ==
$type &&
isset(
$vars[
$name])) {
$result =
$vars[
$name];
//解析执行类构造函数时执行
}
elseif (
$param->
isDefaultValueAvailable()) {
//getDefaultValue拿到构造函数参数的值
$result =
$param->
getDefaultValue();
}
else {
throw
new
\InvalidArgumentException(
'method param miss:' .
$name);
}
return
$result;
}
/**
* 执行调用分发
*
@access
protected
*
@param
array
$dispatch 调用信息
*
@param
array
$config 配置信息
*
@return
Response
|
mixed
*
@throws
\InvalidArgumentException
*/
protected
static
function
exec(
$dispatch,
$config)
{
switch (
$dispatch[
'type']) {
case
'redirect':
// 重定向跳转
$data =
Response::
create(
$dispatch[
'url'],
'redirect')
->
code(
$dispatch[
'status']);
break;
case
'module':
// 模块/控制器/操作
$data =
self::
module(
$dispatch[
'module'],
$config,
isset(
$dispatch[
'convert']) ?
$dispatch[
'convert'] :
null
);
break;
case
'controller':
// 执行控制器操作
$vars =
array_merge(
Request::
instance()->
param(),
$dispatch[
'var']);
$data =
Loader::
action(
$dispatch[
'controller'],
$vars,
$config[
'url_controller_layer'],
$config[
'controller_suffix']
);
break;
case
'method':
// 回调方法
$vars =
array_merge(
Request::
instance()->
param(),
$dispatch[
'var']);
$data =
self::
invokeMethod(
$dispatch[
'method'],
$vars);
break;
case
'function':
// 闭包
$data =
self::
invokeFunction(
$dispatch[
'function']);
break;
case
'response':
// Response 实例
$data =
$dispatch[
'response'];
break;
default:
throw
new
\InvalidArgumentException(
'dispatch type not support');
}
return
$data;
}
/**
* 执行模块
*
@access
public
*
@param
array
$result 模块/控制器/操作
*
@param
array
$config 配置参数
*
@param
bool
$convert 是否自动转换控制器和操作名
*
@return
mixed
*
@throws
HttpException
*/
// 开始调度,获取地址栏参数信息,$config为\thinkphp\convention.php里的配置信息
public
static
function
module(
$result,
$config,
$convert =
null)
{
//判断传递过来的$result是否为字符串
if (
is_string(
$result)) {
$result =
explode(
'/',
$result);
}
//实例化think\Request, 该类在thinkphp\library\think目录下Request.php
$request =
Request::
instance();
//判断application目录下的config.php参数中的app_multi_module是否为true,
if(
$config[
'app_multi_module']){
/* 多模块部署,$result[0] 是否有参数,没有则通过\thinkphp\convention.php下的default_module参数
获取,config可设置default_module,就是默认模块,$module默认值为index */
$module =
strip_tags(
strtolower(
$result[
0] ?:
$config[
'default_module']));
$bind =
Route::
getBind(
'module');
// 先设置available变量,判断模块是否允许访问
$available =
false;
if (
$bind) {
// 绑定模块
list(
$bindModule) =
explode(
'/',
$bind);
if (
empty(
$result[
0])) {
$module =
$bindModule;
$available =
true;
}
elseif (
$module ==
$bindModule) {
$available =
true;
}
/* 寻找配置中的deny_module_list是否与$module的值对应,deny_module_list在配置application下的
config.php可设置, deny_module_list默认是common,也就是commmon这个模块禁止访问, 不执行以下语句,
那$available的值为false ,并且application目录下$module文件夹是否存在 */
}
elseif (!
in_array(
$module,
$config[
'deny_module_list']) &&
is_dir(APP_PATH .
$module)) {
//deny_module_list数组中没有找到该模块值,并且该模块文件是存在的,那$available的值会为true
$available =
true;
}
// 模块初始化 ,module和available有值时执行,上面语句将 $available设置为true,
// $available的值就为1,并且$module有值,则执行以下语句
if (
$module &&
$available) {
// 初始化模块 实例化thinkphp\library\think目录下Request.php中Request类的module方法
$request->
module(
$module);
//获取\thinkphp\convention.php和\application\extra\queue.php的全部配置信息
$config =
self::
init(
$module);
// 模块请求缓存检查
$request->
cache(
$config[
'request_cache'],
$config[
'request_cache_expire'],
$config[
'request_cache_except']
);
}
else {
//找不到模块或模块文件不存在,抛出异常
throw
new
HttpException(
404,
'module not exists:' .
$module);
}
}
else {
// 单一模块部署
$module =
'';
$request->
module(
$module);
}
// 设置默认过滤机制
$request->
filter(
$config[
'default_filter']);
// 当前模块路径 存入当前类$modulePath属性中
App::
$modulePath = APP_PATH . (
$module ?
$module . DS :
'');
// 是否自动转换控制器和操作名 is_bool — 检测变量是否是布尔型
$convert =
is_bool(
$convert) ?
$convert :
$config[
'url_convert'];
// 获取控制器名 application下的config.php中的参数default_controller的值,默认为index
$controller =
strip_tags(
$result[
1] ?:
$config[
'default_controller']);
//判断是否为自动转换控制器
$controller =
$convert ?
strtolower(
$controller) :
$controller;
// 获取操作名 application下的config.php中的参数default_action的值,默认为index
$actionName =
strip_tags(
$result[
2] ?:
$config[
'default_action']);
if (!
empty(
$config[
'action_convert'])) {
$actionName =
Loader::
parseName(
$actionName,
1);
}
else {
$actionName =
$convert ?
strtolower(
$actionName) :
$actionName;
}
// 设置当前请求的控制器、操作
$request->
controller(
Loader::
parseName(
$controller,
1))->
action(
$actionName);
// 开始监听模块
Hook::
listen(
'module_init',
$request);
/*
$controller 控制器名
$config['url_controller_layer'] = 'controller';
$config['controller_suffix'] = 'false';
//空控制器名
$config['empty_controller'] = 'Error';
先开始执行类的反射 \thinkphp\library\think\Loader.php 中Loader类的controller方法
*/
try {
//返回app\index\controller\Index类的实例
$instance =
Loader::
controller(
$controller,
$config[
'url_controller_layer'],
$config[
'controller_suffix'],
$config[
'empty_controller']
);
}
catch (
ClassNotFoundException
$e) {
throw
new
HttpException(
404,
'controller not exists:' .
$e->
getClass());
}
// 获取当前操作名 action_suffix操作方法后缀,\application\config.php配置
$action =
$actionName .
$config[
'action_suffix'];
$vars = [];
//is_callable — 检测参数是否为合法的可调用结构
if (
is_callable([
$instance,
$action])) {
// 执行操作方法
$call = [
$instance,
$action];
// 严格获取当前操作方法名 ReflectionMethod 报告了一个方法的有关信息
$reflect =
new
\ReflectionMethod(
$instance,
$action);
//获取操作名(方法名)
$methodName =
$reflect->
getName();
$suffix =
$config[
'action_suffix'];
$actionName =
$suffix ?
substr(
$methodName,
0, -
strlen(
$suffix)) :
$methodName;
//把操作方法存入Request类action 属性中
$request->
action(
$actionName);
}
elseif (
is_callable([
$instance,
'_empty'])) {
// 空操作
$call = [
$instance,
'_empty'];
$vars = [
$actionName];
}
else {
// 操作不存在
throw
new
HttpException(
404,
'method not exists:' .
get_class(
$instance) .
'->' .
$action .
'()');
}
//监听执行类
Hook::
listen(
'action_begin',
$call);
//执行反射类中的方法 当前类的invokeMethod方法
return
self::
invokeMethod(
$call,
$vars);
}
/**
* URL路由检测(根据PATH_INFO)
*
@access
public
*
@param
\think\
Request
$request 请求实例
*
@param
array
$config 配置信息
*
@return
array
*
@throws
\think\
Exception
*/
//解析地址栏参数
public
static
function
routeCheck(
$request,
array
$config)
{
//获取访问的地址栏参数
$path =
$request->
path();
/* $depr='/',pathinfo_depr为'/' \application\config.php中可查看pathinfo_depr */
$depr =
$config[
'pathinfo_depr'];
$result =
false;
/*路由检测 如果当前类$routeCheck不为空,$check值为当前类$routeCheck的值,否则为true
\application\config.php中可查看url_route_on */
$check = !
is_null(
self::
$routeCheck) ?
self::
$routeCheck :
$config[
'url_route_on'];
//$check有值时执行
if (
$check) {
// 开启路由 \runtime\route.php存在时执行
if (
is_file(RUNTIME_PATH .
'route.php')) {
// 读取路由缓存
$rules =
include RUNTIME_PATH .
'route.php';
is_array(
$rules) &&
Route::
rules(
$rules);
//\runtime\route.php不存在时执行
}
else {
/* // 路由配置文件(支持配置多个)
'route_config_file' => ['route'],
route_config_file 可在 \application\config.php中配置
*/
$files =
$config[
'route_config_file'];
//可载入多个路由配置文件
foreach (
$files as
$file) {
if (
is_file(CONF_PATH .
$file . CONF_EXT)) {
// 导入路由配置 默认只导入/application/route.php,载入多个在route_config_file中配置
$rules =
include CONF_PATH .
$file . CONF_EXT;
/*首先判断$rules变量是否是数组,如果是则执行
\thinkphp\library\think\Route.php Route中import类,并将配置作为参数传入 */
is_array(
$rules) &&
Route::
import(
$rules);
}
}
}
/* 路由检测(根据路由定义返回不同的URL调度)
$request 为 \think\Request的实例;
$path 经框架处理,$path = public/index.php/后面的参数 例如:
访问index模块下index控制器里的index方法,则$path = 'index/index/index';
$depr = '/'
$config['url_domain_deploy'] = 'false'; (\application\config/php可以配置url_domain_deploy)
执行Route类中静态方法check,并把上述变量传入
*/
$result =
Route::
check(
$request,
$path,
$depr,
$config[
'url_domain_deploy']);
$must = !
is_null(
self::
$routeMust) ?
self::
$routeMust :
$config[
'url_route_must'];
if (
$must &&
false ===
$result) {
// 路由无效
throw
new
RouteNotFoundException();
}
}
// 路由无效 解析模块/控制器/操作/参数... 支持控制器自动搜索
if (
false ===
$result) {
//执行当前Route类的parseUrl方法,返回模块控制器和方法名
$result =
Route::
parseUrl(
$path,
$depr,
$config[
'controller_auto_search']);
}
return
$result;
}
/**
* 设置应用的路由检测机制
*
@access
public
*
@param
bool
$route 是否需要检测路由
*
@param
bool
$must 是否强制检测路由
*
@return
void
*/
public
static
function
route(
$route,
$must =
false)
{
self::
$routeCheck =
$route;
self::
$routeMust =
$must;
}
}