了解执行过程
我们经常会好奇配置好路由、写好业务代码后,为什么在URL上输出指定内容,就是执行到业务代码呢?
App::run()
// 模块/控制器绑定
if (defined('BIND_MODULE')) {
BIND_MODULE && Route::bind(BIND_MODULE);
} elseif ($config['auto_bind_module']) { //入口自动绑定模块 默认为false
// 入口自动绑定
$name = pathinfo($request->baseFile(), PATHINFO_FILENAME);
if ($name && 'index' != $name && is_dir(APP_PATH . $name)) {
Route::bind($name);
}
}
如果先定义一个常量BIND_MODULE或在config.php内配置了auto_bind_module项,则调用Route::bind()对模块进行绑定
//执行逻辑程序,获取返回值
$data = self::exec($dispatch, $config);
------------------------------------------------------------
protected static function exec($dispatch, $config)
{
switch ($dispatch['type']) {
case 'redirect': // 重定向跳转
//重定向,url地址将更换
//$dispatch可以传入http://完整地址跳转到其他网站
//也可以传入控制器+方法跳转到内部其他逻辑
$data = Response::create($dispatch['url'], 'redirect')
->code($dispatch['status']);
break;
case 'module': // 模块/控制器/操作
//多模块配置,执行业务逻辑
$data = self::module(
$dispatch['module'], //模块信息
$config, //配置项
isset($dispatch['convert']) ? $dispatch['convert'] : null
);
//$data 为控制器方法的返回值
break;
case 'controller': // 执行控制器操作
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
//Loader::action() 执行某个控制器或当前控制器的 指定方法,此处单纯传入控制器运行有误
//$dispatch['controller'] 1、携带模块+控制器+方法名 2、携带方法名 (前提是Request的controller被赋值)
$data = Loader::action(
$dispatch['controller'],
$vars,
$config['url_controller_layer'],
$config['controller_suffix']
);
break;
case 'method': // 回调方法
//直接执行某个类的方法
//$dispatch['method'] 可以是一个数组,包含类实例对象,和方法名
//也可以是传入静态方法定义规则的字符串
$vars = array_merge(Request::instance()->param(), $dispatch['var']);
$data = self::invokeMethod($dispatch['method'], $vars);
break;
case 'function': // 闭包
//比如在route.php 定义一个路由指向到闭包function,会执行这句
$data = self::invokeFunction($dispatch['function']);
break;
case 'response': // Response 实例
$data = $dispatch['response'];
break;
default:
throw new \InvalidArgumentException('dispatch type not support');
}
return $data;
}
根据$dispatch数组内的type字段,来区分业务的实际执行类型
redirect:重定向跳转
module:模块控制器方法执行
controller:执行控制器操作
method:回调方法
function:闭包
response:Response实例
从个人的应用角度来说,目前用到的是module和function两种。
module模块运行
case 'module': // 模块/控制器/操作
//多模块配置,执行业务逻辑
$data = self::module(
$dispatch['module'], //模块信息
$config, //配置项
isset($dispatch['convert']) ? $dispatch['convert'] : null
);
//$data 为控制器方法的返回值
break;
这种是使用场景最多的,我们一般通过URL访问都是定向访问指定模块、指定控制器、指定方法运行。
$dispatch[‘module’] 包含"模块信息",这个"模块"并不单单表示框架的模块,而是路由相关的整个信息
$config是配置项
$dispatch[‘convert’]来设置传入的pathinfo信息是否自动转换
if ($config['app_multi_module']) {
//TODO ...........
} else {
// 单一模块部署
$module = '';
$request->module($module);
}
根据app_multi_module配置项来区分是否是多模块部署模式,一般来说访问地址格式是module/controller/method,在application下可以构造多个模块目录,可以在各个模块内继续生成controller等等彼此不相关的控制器。如果我们想在一个项目文件下实现多套业务逻辑,比如一个模块是api接口,一个模块是web站点,那么这将是很好的分割方式。
而如果使用单模块的模式,就没有这么复杂,application下有且有一个可被直接访问的controller结构。
那么多模块部署做了什么特殊的工作呢
/*
* 若模块名不存在,则获取配置文件中的默认模块名
* 如访问:http://localhost/thinkphp5/public
* array(
* 0 => '',
* 1 => null,
* 2 => null
* */
//$result[0] 不存在则取出 配置中的default_module作为默认模块
$module = strip_tags(strtolower($result[0] ?: $config['default_module']));
//如果请求未传入pathinfo相关信息,框架会使用default_module配置项作为默认模块
//获取绑定的模块名
$bind = Route::getBind('module');
$available = false;
if ($bind) {
// 绑定模块
list($b