ThinkPHP5源碼分析之分發(fā)輸出(4)

接上章,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í)行流程就走完了。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,562評論 19 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,040評論 25 709
  • Address:https://www.zybuluo.com/XiangZhou/note/208532 Exp...
    天蠍蒗漫閱讀 11,623評論 2 55
  • 天空的陰霾隨著雨水的沖刷漸漸散去,空氣中環(huán)繞著清新的氣息,突然,那片云裂開了,一道道白光從里面探出了頭。天晴了...
    貝殼里的魚閱讀 329評論 0 0
  • 地雷花 我一直以來是不太喜歡花的人,直到人到中年以后,才對鮮花有了點感覺,覺得它們的存在適當?shù)狞c綴了人生,...
    唏里呼嚕閱讀 568評論 2 1

友情鏈接更多精彩內(nèi)容