接上章,App獲取調(diào)度后進行分發(fā)以及瀏覽器的響應輸出,見代碼:
function fun(Request $request = null) {
……
//見源碼分析app(3)
……
switch ($dispatch['type']) {
case 'redirect': // 執(zhí)行重定向跳轉(zhuǎn)
$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': // 執(zhí)行控制器操作
$data = Loader::action($dispatch['controller'], $dispatch['params']);
break;
case 'method':
// 執(zhí)行回調(diào)方法
$data = self::invokeMethod($dispatch['method'], $dispatch['params']);
break;
case 'function':
// 執(zhí)行閉包
$data = self::invokeFunction($dispatch['function'], $dispatch['params']);
break;
case 'response':
$data = $dispatch['response'];
break;
……
}catch (HttpResponseException $exception) {
$data = $exception->getResponse();
}
//清空類的實例化
Loader::clearInstance();
//輸出數(shù)據(jù)到客戶端
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');
//創(chuàng)建響應的一個對象 詳細見Response類
$response = Response::create($data, $type);
}else{
$response = Response::create();
}
……
return $response;
}
首先我們按照正常請求路由執(zhí)行module,也是App類內(nèi)方法,見代碼:
function module($result, $config, $convert = null){
//沒的說,既然是個請求,例行單例下后續(xù)要用到
$request = Request::instance();
//配置,多模塊的部署
if ($config['app_multi_module']) {
//默認模塊index 如果是多模塊部署,url不代模塊參數(shù),就是默認模塊為當前模塊咯
$module = strip_tags(strtolower($result[0] ?: $config['default_module']));
//這里其實是route::bind()綁定模塊,更簡單的url訪問。
/*
例:
我在公共模塊(先加載) Route::bind('index');
那么就是說綁定當前模塊到index模塊
*/
$bind = Route::getBind('module');
//模塊綁定邏輯是否執(zhí)行標記(我也不知道咋描述,姑且用一個自我感覺高大上的描述吧,哈哈)
$available = false ;
if ($bind) {
if ($module == $bindModule) {
$available = true;
}
}elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) {
//如果這個模塊沒有在禁用模塊里面 并且也存在 那說有這個模塊,必須執(zhí)行啊。
$available = true;
}
if ($module && $available) {
//填充Request對象module
$request->module($module);
$config = self::init($module);
} else {
//拋出異常 module not exists;
}
}else{
$module = '';
$request->module($module);
}
// 是否自動轉(zhuǎn)換控制器和操作名
/*
url_convert 官方文檔是這樣說的:關閉URL中控制器和操作名的自動轉(zhuǎn)換
如果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其實就是進行當前控制器的實例化,后面進行反射綁定執(zhí)行action
$instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']);
//封裝的反射 下面詳細說
$data = self::invokeMethod([$instance,$action]);
}catch (\ReflectionException $e) {
//魔術方法,如果請求的action不存在,如果控制器開發(fā)者有自定義一個_empty()那就執(zhí)行咯
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 = []) {
//這里其實就是獲取請求的參數(shù)(post/get/put……)詳細見Request類
$vars = Request::instance()->param();
//兩種反射
/*
① [$controller, $action] 類/方法
② [$static_function] 靜態(tài)方法
反射:ReflectionMethod:通過PHP代碼,就可以得到某object的所有信息,并且可以和它交互,巴拉巴拉的
說白了,就是執(zhí)行類方法里的方法
*/
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);
}
//綁定參數(shù) $vars 為剛才Request::param() 獲取的請求參數(shù)
$args = self::bindParams($reflect, $vars);
//官方文檔說明:使用數(shù)組給方法傳送參數(shù),并執(zhí)行他。
return $reflect->invokeArgs(isset($class) ? $class : null, $args);
}
function bindParams($reflect, $vars){
$args = [];
// 判斷數(shù)組類型 數(shù)字數(shù)組時按順序綁定參數(shù)
/*
request 請求參數(shù):
① ['params1' => 'value1', 'params2' => 'value2' ,……] 俗說就是關聯(lián)數(shù)組啦
② ['params_val2', 'params_val2'] 當然是索引啦。
如果key($vals)為1 那說明是索引的 按照controller類的action方法機型順序綁定參數(shù)
如果是關聯(lián)的 根據(jù)參數(shù)名對應綁定
*/
$type = key($vars) === 0 ? 1 : 0;
//獲取類方法的參數(shù)個數(shù)
if ($reflect->getNumberOfParameters() > 0) {
//獲取參數(shù) 返回的是個對象集
$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])) { //一個個按著嚴格參數(shù)名來
$args[] = $vars[$name];
} elseif ($param->isDefaultValueAvailable()) { //如果這個值不是必須參數(shù) 默認值附上
$args[] = $param->getDefaultValue();
} else {
//gg method param miss
}
}
//過濾掉表單參數(shù)中的表達式 這里附上Request::filterExp
array_walk_recursive($args, [Request::instance(), 'filterExp']);
}
}
Request::filterExp
function filterExp(&$value){
//看見沒 是不是很像數(shù)據(jù)庫里的各種表達式哇
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 .= ' ';
}
}
至此,一個請求的主動執(zhí)行流程就走完了。