接上章,App获取调度后进行分发以及浏览器的响应输出,见代码:
function fun(Request $request = null) {
……
//见源码分析app(3)
……
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': // 执行控制器操作
$data = Loader::action($dispatch['controller'], $dispatch['params']);
break;
case 'method':
// 执行回调方法
$data = self::invokeMethod($dispatch['method'], $dispatch['params']);
break;
case 'function':
// 执行闭包
$data = self::invokeFunction($dispatch['function'], $dispatch['params']);
break;
case 'response':
$data = $dispatch['response'];
break;
……
}catch (HttpResponseException $exception) {
$data = $exception->getResponse();
}
//清空类的实例化
Loader::clearInstance();
//输出数据到客户端
if ($data instanceof Response) {
$response = $data;
}elseif (!is_null($data)) {
//获取请求类型以及配置返回格式类型等
$isAjax = $request->isAjax();
$type = $isAjax ? Config::get('default_ajax_return') : Config::get('default_return_type');
//创建响应的一个对象 详细见Response类
$response = Response::create($data, $type);
}else{
$response = Response::create();
}
……
return $response;
}
首先我们按照正常请求路由执行module,也是App类内方法,见代码:
function module($result, $config, $convert = null){
//没的说,既然是个请求,例行单例下后续要用到
$request = Request::instance();
//配置,多模块的部署
if ($config['app_multi_module']) {
//默认模块index 如果是多模块部署,url不代模块参数,就是默认模块为当前模块咯
$module = strip_tags(strtolower($result[0] ?: $config['default_module']));
//这里其实是route::bind()绑定模块,更简单的url访问。
/*
例:
我在公共模块(先加载) Route::bind('index');
那么就是说绑定当前模块到index模块
*/
$bind = Route::getBind('module');
//模块绑定逻辑是否执行标记(我也不知道咋描述,姑且用一个自我感觉高大上的描述吧,哈哈)
$available = false ;
if ($bind) {
if ($module == $bindModule) {
$available = true;
}
}elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) {
//如果这个模块没有在禁用模块里面 并且也存在 那说有这个模块,必须执行啊。
$available = true;
}
if ($module && $available) {
//填充Request对象module
$request->module($module);
$config = self::init($module);
} else {
//抛出异常 module not exists;
}
}else{
$module = '';
$request->module($module);
}
// 是否自动转换控制器和操作名
/*
url_convert 官方文档是这样说的:关闭URL中控制器和操作名的自动转换
如果controller TestConvert 那么以下
http://server/Index/TestConvert/index or http://server/Index/test_convert/index都是有效的
http://server/Index/testconvert/index无效的 我还是建议搭建开启,不然url太难看了
*/
$convert = is_bool($convert) ? $convert : $config['url_convert'];
// 获取控制器名
$controller = strip_tags($result[1] ?: $config['default_controller']);
// 获取操作名
$actionName = strip_tags($result[2] ?: $config['default_action']);
//填充controller action
$request->controller($controller)->action($actionName);
try {
//controller其实就是进行当前控制器的实例化,后面进行反射绑定执行action
$instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']);
//封装的反射 下面详细说
$data = self::invokeMethod([$instance,$action]);
}catch (\ReflectionException $e) {
//魔术方法,如果请求的action不存在,如果控制器开发者有自定义一个_empty()那就执行咯
if (method_exists($instance, '_empty')) {
$method = new \ReflectionMethod($instance, '_empty');
$data = $method->invokeArgs($instance, [$action, '']);
} else {
//gg method(action)not found
}
}
return $data;
}
function invokeMethod($method, $vars = []) {
//这里其实就是获取请求的参数(post/get/put……)详细见Request类
$vars = Request::instance()->param();
//两种反射
/*
① [$controller, $action] 类/方法
② [$static_function] 静态方法
反射:ReflectionMethod:通过PHP代码,就可以得到某object的所有信息,并且可以和它交互,巴拉巴拉的
说白了,就是执行类方法里的方法
*/
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);
}
//绑定参数 $vars 为刚才Request::param() 获取的请求参数
$args = self::bindParams($reflect, $vars);
//官方文档说明:使用数组给方法传送参数,并执行他。
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
}
function bindParams($reflect, $vars){
$args = [];
// 判断数组类型 数字数组时按顺序绑定参数
/*
request 请求参数:
① ['params1' => 'value1', 'params2' => 'value2' ,……] 俗说就是关联数组啦
② ['params_val2', 'params_val2'] 当然是索引啦。
如果key($vals)为1 那说明是索引的 按照controller类的action方法机型顺序绑定参数
如果是关联的 根据参数名对应绑定
*/
$type = key($vars) === 0 ? 1 : 0;
//获取类方法的参数个数
if ($reflect->getNumberOfParameters() > 0) {
//获取参数 返回的是个对象集
$params = $reflect->getParameters();
foreach ($params as $param) {
$name = $param->getName();
$class = $param->getClass();
//没啥用
if ($class && 'think\Request' == $class->getName()) {
$args[] = Request::instance();
} elseif (1 == $type && !empty($vars)) { //一个个按着顺序来
$args[] = array_shift($vars);
} elseif (0 == $type && isset($vars[$name])) { //一个个按着严格参数名来
$args[] = $vars[$name];
} elseif ($param->isDefaultValueAvailable()) { //如果这个值不是必须参数 默认值附上
$args[] = $param->getDefaultValue();
} else {
//gg method param miss
}
}
//过滤掉表单参数中的表达式 这里附上Request::filterExp
array_walk_recursive($args, [Request::instance(), 'filterExp']);
}
}
Request::filterExp
function filterExp(&$value){
//看见没 是不是很像数据库里的各种表达式哇
if (is_string($value) && preg_match('/^(EXP|NEQ|GT|EGT|LT|ELT|OR|XOR|LIKE|NOTLIKE|NOT BETWEEN|NOTBETWEEN|BETWEEN|NOTIN|NOT IN|IN)$/i', $value)) {
$value .= ' ';
}
}
至此,一个请求的主动执行流程就走完了。
网友评论