[TOC]
* * * * *
## 1 应用调度源代码(thinkphp/library/think/App::run())
~~~
switch (self::$dispatch['type']) {
case 'redirect':
header('Location: ' . self::$dispatch['url'], true, self::$dispatch['status']);
break;
case 'module':
$data = self::module(self::$dispatch['module'], $config);
break;
case 'controller':
$data = Loader::action(self::$dispatch['controller'], self::$dispatch['params']);
break;
case 'method':
$data = self::invokeMethod(self::$dispatch['method'], self::$dispatch['params']);
break;
case 'function':
$data = self::invokeFunction(self::$dispatch['function'], self::$dispatch['params']);
break;
default:
throw new Exception('dispatch type not support', 10008);
}
~~~
## 2 应用调度分析
### 1 路由解析
~~~
switch (self::$dispatch['type']) {}
~~~
$dispatch['type']是App::run()中
经过self::route(),self::dispatch()后得到
* * * * *
~~~
if (empty(self::$dispatch['type'])) {
self::route($config);
}
~~~
>[info] 解析url,生成$dispatch
检查self::$dispatch['type'],
调用self::route()解析url,生成$dispatch。
* * * * *
~~~
public static function route(array $config)
{
self::parsePathinfo($config);
if (empty($_SERVER['PATH_INFO'])) {
$_SERVER['PATH_INFO'] = '';
define('__INFO__', '');
define('__EXT__', '');
} else {
$_SERVER['PATH_INFO'] = trim($_SERVER['PATH_INFO'], '/');
define('__INFO__', $_SERVER['PATH_INFO']);
define('__EXT__', strtolower(pathinfo($_SERVER['PATH_INFO'], PATHINFO_EXTENSION)));
if ($config['url_deny_suffix'] && preg_match('/\.(' . $config['url_deny_suffix'] . ')$/i', __INFO__)) {
throw new Exception('url suffix deny');
}
$_SERVER['PATH_INFO'] = preg_replace($config['url_html_suffix'] ? '/\.(' . trim($config['url_html_suffix'], '.') . ')$/i' : '/\.' . __EXT__ . '$/i', '', __INFO__);
}
$depr = $config['pathinfo_depr'];
$result = false;
if (APP_ROUTE_ON && !empty($config['url_route_on'])) {
if (!empty($config['route'])) {
Route::register($config['route']);
}
$result = Route::check($_SERVER['PATH_INFO'], $depr, !IS_CLI ? $config['url_domain_deploy'] : false);
if (APP_ROUTE_MUST && false === $result && $config['url_route_must']) {
throw new Exception('route not define ');
}
}
if (false === $result) {
$result = Route::parseUrl($_SERVER['PATH_INFO'], $depr);
}
self::dispatch($result);
}
~~~
self::route()中,根据配置参数,解析url。
最后调用self::dispatch(),设置$disptach 。
具体分析见[附:应用启动文件](http://www.kancloud.cn/zmwtp/tp5/119429)
* * * * *
~~~
public static function dispatch($dispatch)
{
self::$dispatch = $dispatch;
}
~~~
在self::route中调用,设置调度类型$dispatch
* * * * *
### 2 五种调度类型
>[info] Redirect类型调度
~~~
case 'redirect':
header('Location: ' . self::$dispatch['url'], true, self::$dispatch['status']);
break;
~~~
$dispatch['type']为**redirect**时的应用调度
直接跳转到**self::$dispatch['url']**
>[info] Module调度类型
~~~
case 'module':
$data = self::module(self::$dispatch['module'], $config);
break;
~~~
$dispatch['type']为**module**时的应用调度
调用**self::module()**
>[info] Controller调度类型
~~~
case 'controller':
$data = Loader::action(self::$dispatch['controller'], self::$dispatch['params']);
break;
~~~
$dispatch['type']为**controller**时的调度
调用自动加载器**Loader::action()**
>[info] Method调度类型
~~~
case 'method':
$data = self::invokeMethod(self::$dispatch['method'], self::$dispatch['params']);
break;
~~~
$dispatch['type']为**method**时的调度
调用**self::invokeMethod()**
>[info] Function调度类型
~~~
case 'function':
$data = self::invokeFunction(self::$dispatch['function'], self::$dispatch['params']);
break;
~~~
$dispatch['type']为**function**时的调度
调用**self::invokeFunction()**
~~~
default:
throw new Exception('dispatch type not support', 10008);
~~~
超出以上调度类型的直接抛出异常报错
* * * * *
## 3 调度方法实现
上面的五种调度类型中
除了redirect,controller调度类型外的其余三种调度类型
都是调用的App内的静态方法
moudle调度类型的self::module()
method调度类型的self::invokeMethod()
function调度类型的self::invokeFunction()
三个静态方法的实现原理基本相同,
依次为调用参数分析,反射回调相应方法,
关于反射原理见基本原理的[php的反射](http://www.kancloud.cn/zmwtp/tp5/119469)
下面按照源代码顺序依次分析这三个静态方法:
### 1 self::invokeFunction()
~~~
public static function invokeFunction($function, $vars = [])
{
$reflect = new \ReflectionFunction($function);
$args = self::bindParams($reflect, $vars);
APP_DEBUG && Log::record('[ RUN ] ' . $reflect->getFileName() . '[ ' . var_export($vars, true) . ' ]', 'info');
return $reflect->invokeArgs($args);
}
~~~
> $function 调用的函数名
> $vars 调用函数的参数
`$reflect = new \ReflectionFunction($function);`
创建$function的反射函数类
`$args = self::bindParams($reflect, $vars);`
创建反射函数的参数
`APP_DEBUG && Log::record('[ RUN ] ' . $reflect->getFileName() . '[ ' . var_export($vars, true) . ' ]', 'info');`
日志记录调度运行
`return $reflect->invokeArgs($args);`
使用反射函数类调用函数,并返回结果保存到App::run()中的$data~~~
* * * * *
### 2 self::invokeMethod()
~~~
public static function invokeMethod($method, $vars = [])
{
if (empty($vars)) {
switch (REQUEST_METHOD) {
case 'POST':
$vars = array_merge($_GET, $_POST);
break;
case 'PUT':
parse_str(file_get_contents('php://input'), $vars);
break;
default:
$vars = $_GET;
}
}
if (is_array($method)) {
$class = is_object($method[0]) ? $method[0] : new $method[0];
$reflect = new \ReflectionMethod($class, $method[1]);
} else {
$reflect = new \ReflectionMethod($method);
}
$args = self::bindParams($reflect, $vars);
APP_DEBUG && Log::record('[ RUN ] ' . $reflect->getFileName() . '[ ' . var_export($args, true) . ' ]', 'info');
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
}
~~~
> $method 调用的方法
> $vars 调用的方法的参数
~~~
if (empty($vars)) {
switch (REQUEST_METHOD) {
case 'POST':
$vars = array_merge($_GET, $_POST);
break;
case 'PUT':
parse_str(file_get_contents('php://input'), $vars);
break;
default:
$vars = $_GET;
}
}
~~~
合并$_POST $_GET $_input等参数到$vars
~~~
if (is_array($method)) {
$class = is_object($method[0]) ? $method[0] : new $method[0];
$reflect = new \ReflectionMethod($class, $method[1]);
} else {
$reflect = new \ReflectionMethod($method);
}
~~~
检查$method
如果为数组则是调用对象的方法
如果不是数组则是调用静态方法
创建$method反射方法类
`$args = self::bindParams($reflect, $vars);`
同上创建参数
`APP_DEBUG && Log::record('[ RUN ] ' . $reflect->getFileName() . '[ ' . var_export($args, true) . ' ]', 'info');`
同上记录调度运行日志
`return $reflect->invokeArgs(isset($class) ? $class : null, $args);`
同上获取调用返回值,
* * * * *
### 3 self::module()
~~~
private static function module($result, $config)
{
if (APP_MULTI_MODULE) {
$module = strtolower($result[0] ?: $config['default_module']);
if ($maps = $config['url_module_map']) {
if (isset($maps[$module])) {
define('MODULE_ALIAS', $module);
$module = $maps[MODULE_ALIAS];
} elseif (array_search($module, $maps)) {
$module = '';
}
}
define('MODULE_NAME', strip_tags($module));
if (MODULE_NAME && !in_array(MODULE_NAME, $config['deny_module_list']) && is_dir(APP_PATH . MODULE_NAME)) {
define('MODULE_PATH', APP_PATH . MODULE_NAME . DS);
define('VIEW_PATH', MODULE_PATH . VIEW_LAYER . DS);
self::initModule(MODULE_NAME, $config);
} else {
throw new Exception('module [ ' . MODULE_NAME . ' ] not exists ', 10005);
}
} else {
define('MODULE_NAME', '');
define('MODULE_PATH', APP_PATH);
define('VIEW_PATH', MODULE_PATH . VIEW_LAYER . DS);
}
$controllerName = strip_tags($result[1] ?: Config::get('default_controller'));
define('CONTROLLER_NAME', Config::get('url_controller_convert') ? strtolower($controllerName) : $controllerName);
$actionName = strip_tags($result[2] ?: Config::get('default_action'));
define('ACTION_NAME', Config::get('url_action_convert') ? strtolower($actionName) : $actionName);
if (!preg_match('/^[A-Za-z](\/|\.|\w)*$/', CONTROLLER_NAME)) {
throw new Exception('illegal controller name:' . CONTROLLER_NAME, 10000);
}
$instance = Loader::controller(CONTROLLER_NAME, '', Config::get('empty_controller'));
$action = ACTION_NAME . Config::get('action_suffix');
try {
$call = [$instance, $action];
APP_HOOK && Hook::listen('action_begin', $call);
if (!preg_match('/^[A-Za-z](\w)*$/', $action)) {
throw new Exception('illegal action name :' . ACTION_NAME, 10001);
}
$data = self::invokeMethod($call);
} catch (\ReflectionException $e) {
if (method_exists($instance, '_empty')) {
$method = new \ReflectionMethod($instance, '_empty');
$data = $method->invokeArgs($instance, [$action, '']);
APP_DEBUG && Log::record('[ RUN ] ' . $method->getFileName(), 'info');
} else {
throw new Exception('method [ ' . (new \ReflectionClass($instance))->getName() . '->' . $action . ' ] not exists ', 10002);
}
}
return $data;
}
~~~
> $result 调用的模块信息
> $config 调用相关的配置信息
>[info] 1 模块参数处理
`if (APP_MULTI_MODULE) {}`
根据全局变量APP_MULTI_MODULE分为多模块部署和单模块部署
>[info] 1-1多模块部署
`$module = strtolower($result[0] ?: $config['default_module']);`
将调用的模块名称转换为小写
~~~
if ($maps = $config['url_module_map']) {
if (isset($maps[$module])) {
define('MODULE_ALIAS', $module);
$module = $maps[MODULE_ALIAS];
} elseif (array_search($module, $maps)) {
$module = '';
}
~~~
检查全局配置是否有别名配置
`define('MODULE_NAME', strip_tags($module));`
定义全局变量MODULE_NAME,
~~~
if (MODULE_NAME && !in_array(MODULE_NAME, $config['deny_module_list']) && is_dir(APP_PATH . MODULE_NAME)) {
define('MODULE_PATH', APP_PATH . MODULE_NAME . DS);
define('VIEW_PATH', MODULE_PATH . VIEW_LAYER . DS);
self::initModule(MODULE_NAME, $config);
} else {
throw new Exception('module [ ' . MODULE_NAME . ' ] not exists ', 10005);
}
~~~
检查模块是否禁用$config['deny_module_list']
模块对应目录是否存在APP_PATH.MODULE_NAME
定义全局变量MODULE_PATH 模块控制器文件路径
定义全局变量VIEW_PATH 模块视图文件路径
`self::initModule(MODULE_NAME, $config);`
初始化相应模块信息
>[info] 1-2 单一模块部署
~~~
define('MODULE_NAME', '');
define('MODULE_PATH', APP_PATH);
define('VIEW_PATH', MODULE_PATH . VIEW_LAYER . DS);
~~~
定义全局变量MODULE_NAME,MODULE_PATH,VIEW_PATH。
* * * * *
>[info] 2 控制器参数处理
` $controllerName = strip_tags($result[1] ?: Config::get('default_controller'));`
获取控制器名称
` define('CONTROLLER_NAME', Config::get('url_controller_convert') ? strtolower($controllerName) : $controllerName);`
定义全局变量CONTROLLER_NAME 默认为小写的控制器名字
* * * * *
>[info] 3 操作参数处理
`$actionName = strip_tags($result[2] ?: Config::get('default_action'));`
获取操作名称
`define('ACTION_NAME', Config::get('url_action_convert') ? strtolower($actionName) : $actionName);`
定义全局变量ACTION_NAME 默认为小写的操作名字
~~~
if (!preg_match('/^[A-Za-z](\/|\.|\w)*$/', CONTROLLER_NAME)) {
throw new Exception('illegal controller name:' . CONTROLLER_NAME, 10000);
}
~~~
检查控制器名称的合法性
`$instance = Loader::controller(CONTROLLER_NAME, '', Config::get('empty_controller'));`
加载控制器文件并创建控制器实例
`$action = ACTION_NAME . Config::get('action_suffix');`
获取回调控制器的操作
* * * * *
>[info] 4 应用调度运行
* * * * *
>[info] try部分
`$call = [$instance, $action];`
合并控制器对象 方法为数组参数
`APP_HOOK && Hook::listen('action_begin', $call);`
运行方法开始的监听回调
~~~
if (!preg_match('/^[A-Za-z](\w)*$/', $action)) {
throw new Exception('illegal action name :' . ACTION_NAME, 10001);
}
~~~
检查方法名称的合法性
`$data = self::invokeMethod($call);`
使用反射调用相应对象的方法
>[info] catch部分
~~~
if (method_exists($instance, '_empty')) {
$method = new \ReflectionMethod($instance, '_empty');
$data = $method->invokeArgs($instance, [$action, '']);
APP_DEBUG && Log::record('[ RUN ] ' . $method->getFileName(), 'info');
} else {
throw new Exception('method [ ' . (new \ReflectionClass($instance))->getName() . '->' . $action . ' ] not exists ', 10002);
}
~~~
对异常的处理
如果操作不存在 检查_empty方法是否定义
并调用_empty方法
或者返回方法不存在异常
`return $data;`
返回调用的值
* * * * *
### 4 self::bindParams()
~~~
private static function bindParams($reflect, $vars)
{
$args = [];
$keys = array_keys($vars);
$type = array_keys($keys) === $keys ? 1 : 0;
if ($reflect->getNumberOfParameters() > 0) {
$params = $reflect->getParameters();
foreach ($params as $param) {
$name = $param->getName();
if (1 == $type && !empty($vars)) {
$args[] = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) {
$args[] = $vars[$name];
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
throw new Exception('method param miss:' . $name, 10004);
}
}
array_walk_recursive($args, 'think\\Input::filterExp');
}
return $args;
}
~~~
> $reflect 反射类
> $vars 调用参数
~~~
$keys = array_keys($vars);
$type = array_keys($keys) === $keys ? 1 : 0;
~~~
参数的数组类型转换,
~~~
if ($reflect->getNumberOfParameters() > 0) {
$params = $reflect->getParameters();
foreach ($params as $param) {
$name = $param->getName();
if (1 == $type && !empty($vars)) {
$args[] = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) {
$args[] = $vars[$name];
} elseif ($param->isDefaultValueAvailable()) {
$args[] = $param->getDefaultValue();
} else {
throw new Exception('method param miss:' . $name, 10004);
}
}
array_walk_recursive($args, 'think\\Input::filterExp');
}
~~~
将参数$args添加到回调方法的参数$args中
`return $args;`
返回合成的参数
* * * * *
## 4 总结
应用调度总的思路是根据调度类型,通过不同的调度处理,返回调度处理的结果到App::run(),然后调用Response::send()返回到客户端。
**这里的调度是从框架底层到应用业务的跳转核心,需要认真分析。**
- 更新记录
- 概述
- 文件索引
- 函数索引
- 章节格式
- 框架流程
- 前:章节说明
- 主:(index.php)入口
- 主:(start.php)框架引导
- 主:(App.php)应用启动
- 主:(App.php)应用调度
- C:(Controller.php)应用控制器
- M:(Model.php)数据模型
- V:(View.php)视图对象
- 附:(App.php)应用启动
- 附:(base.php)全局变量
- 附:(common.php)模式配置
- 附:(convention.php)全局配置
- 附:(Loader.php)自动加载器
- 附:(Build.php)自动生成
- 附:(Hook.php)监听回调
- 附:(Route.php)全局路由
- 附:(Response.php)数据输出
- 附:(Log.php)日志记录
- 附:(Exception.php)异常处理
- 框架工具
- 另:(helper.php)辅助函数
- 另:(Cache.php)数据缓存
- 另:(Cookie.php)cookie操作
- 另:(Console.php)控制台
- 另:(Debug.php)开发调试
- 另:(Error.php)错误处理
- 另:(Url.php)Url操作文件
- 另:(Loader.php)加载器实例化
- 另:(Input.php)数据输入
- 另:(Lang.php)语言包管理
- 另:(ORM.php)ORM基类
- 另:(Process.php)进程管理
- 另:(Session.php)session操作
- 另:(Template.php)模板解析
- 框架驱动
- D:(\config)配置解析
- D:(\controller)控制器扩展
- D:(\model)模型扩展
- D:(\db)数据库驱动
- D:(\view)模板解析
- D:(\template)模板标签库
- D:(\session)session驱动
- D:(\cache)缓存驱动
- D:(\console)控制台
- D:(\process)进程扩展
- T:(\traits)Trait目录
- D:(\exception)异常实现
- D:(\log)日志驱动
- 使用范例
- 服务器与框架的安装
- 控制器操作
- 数据模型操作
- 视图渲染控制
- MVC开发初探
- 模块开发
- 入口文件定义全局变量
- 运行模式开发
- 框架配置
- 自动生成应用
- 事件与插件注册
- 路由规则注册
- 输出控制
- 多种应用组织
- 综合应用
- tp框架整合后台auto架构快速开发
- 基础原理
- php默认全局变量
- php的魔术方法
- php命名空间
- php的自动加载
- php的composer
- php的反射
- php的trait机制
- php设计模式
- php的系统时区
- php的异常错误
- php的输出控制
- php的正则表达式
- php的闭包函数
- php的会话控制
- php的接口
- php的PDO
- php的字符串操作
- php的curl
- 框架心得
- 心:整体结构
- 心:配置详解
- 心:加载器详解
- 心:输入输出详解
- 心:url路由详解
- 心:模板详解
- 心:模型详解
- 心:日志详解
- 心:缓存详解
- 心:控制台详解
- 框架更新
- 4.20(验证类,助手函数)
- 4.27(新模型Model功能)
- 5.4(新数据库驱动)
- 7.28(自动加载)