url路由分发 php5,ThinkPHP5源码分析之分发输出(4)

接上章,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 .= ' ';

}

}

至此,一个请求的主动执行流程就走完了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值