namespace think;// 放到同一個大包 下面 use think\Config;// 使用 配置類 use think\Env;// 使用環境類 use think\Exception;// 使用異常包 use think\exception\HttpException;// 使用異常包 http包 use think\exception\HttpResponseException;// 使用異常包 http response 包 use think\Hook;// 使用 鉤子 類 use think\Lang;// 使用 語言 類 use think\Loader;// 使用 加載 類 use think\Log;// 使用 日志 類 use think\Request;// 使用 請求 類 use think\Response;// 使用 返回 類 use think\Route;// 使用 路由 類 // 這個類應算是 皇上類瞭 可以調度基本上 全部的資源 /** * App 應用管理 */ class App { /** * @var bool 是否初始化過 */ protected static $init = false;// 初始化 標志位 /** * @var string 當前模塊路徑 */ public static $modulePath;// 初始化 當前 模塊 路徑 /** * @var bool 應用調試模式 */ public static $debug = true;// 應用調試 模式 /** * @var string 應用類庫命名空間 */ public static $namespace = 'app';// 應用 類庫 命名空間 /** * @var bool 應用類庫後綴 */ public static $suffix = false;// 應用 類庫 後綴 /** * @var bool 應用路由檢測 */ protected static $routeCheck;// 應用 路由 檢測 /** * @var bool 嚴格路由檢測 */ protected static $routeMust; // 嚴格 路由檢測 protected static $dispatch;// 路由調度 protected static $file = []; // 文件加載 /** * 執行應用程式 * @access public * @param Request $request Request對象 * @return Response * @throws Exception */ public static function run(Request $request = null) {// thinkphp經過瞭 自動加載、錯誤接管、配置文件預設,終於開始執行瞭。 // 第一步:獲取請求參數 is_null($request) && $request = Request::instance(); // self::$instance = new static($options); 執行瞭 這個 instance // 默認 沒有傳入任何數值,is_null 所以執行 後面 $request = Request::instance(); // 這個寫法 真的 很經典,我喜歡你,老劉,哈哈 // 最終獲取瞭 一個 request 對象 $config = self::initCommon();// 加載 初始化 配置 文件 選項 // 2016-10-09 if (defined('BIND_MODULE')) {// 默認此處是沒有定義的 // 模塊/控制器綁定 BIND_MODULE && Route::bind(BIND_MODULE);//self::$bind = ['type' => $type, $type => $bind]; // 對 Route 的 $ } elseif ($config['auto_bind_module']) {// 默認這裡也是沒有定義的 // 入口自動綁定 $name = pathinfo($request->baseFile(), PATHINFO_FILENAME); if ($name && 'index' != $name && is_dir(APP_PATH . $name)) { Route::bind($name); } } $request->filter($config['default_filter']);// 指定 過濾 參數 為空 直接 返回 try { // 開啟多語言機制 if ($config['lang_switch_on']) { // 獲取當前語言 $request->langset(Lang::detect()); // 加載系統語言包 Lang::load(THINK_PATH . 'lang' . DS . $request->langset() . EXT);// 加載 系統 配置的語言包 if (!$config['app_multi_module']) {// 如果沒有 多模塊配置 Lang::load(APP_PATH . 'lang' . DS . $request->langset() . EXT); } } // 獲取應用調度信息 $dispatch = self::$dispatch;// 默認 為空 if (empty($dispatch)) { // 進行URL路由檢測 $dispatch = self::routeCheck($request, $config);// 獲取調度信息 // 分別 通過 請求 跟 配置 文件 } // 記錄當前調度信息 $request->dispatch($dispatch);// 配置調度 信息 // 記錄路由和請求信息 if (self::$debug) { Log::record('[ ROUTE ] ' . var_export($dispatch, true), 'info'); Log::record('[ HEADER ] ' . var_export($request->header(), true), 'info'); Log::record('[ PARAM ] ' . var_export($request->param(), true), 'info'); }// 存入 信息 到數據, // 監聽app_begin Hook::listen('app_begin', $dispatch);// 這個基本上 什麼事情 都沒幹啊 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']); break; case 'method': // 執行回調方法 $data = self::invokeMethod($dispatch['method']); break; case 'function': // 執行閉包 $data = self::invokeFunction($dispatch['function']); break; case 'response': $data = $dispatch['response']; break; default: throw new \InvalidArgumentException('dispatch type not support'); } } 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::create($data, $type); } else { $response = Response::create(); } // 監聽app_end Hook::listen('app_end', $response);// 監聽 結尾, return $response;// 此處返回 一個默認的類 } /** * 設置當前請求的調度信息 * @access public * @param array|string $dispatch 調度信息 * @param string $type 調度類型 * @return void */ public static function dispatch($dispatch, $type = 'module') {// 賦值 當前資源 self::$dispatch = ['type' => $type, $type => $dispatch]; } /** * 執行函數或者閉包方法 支持參數調用 * @access public * @param string|array|\Closure $function 函數或者閉包 * @param array $vars 變量 * @return mixed */ public static function invokeFunction($function, $vars = []) { $reflect = new \ReflectionFunction($function); $args = self::bindParams($reflect, $vars); // 記錄執行信息 self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); return $reflect->invokeArgs($args); } /** * 調用反射執行類的方法 支持參數綁定 * @access public * @param string|array $method 方法 * @param array $vars 變量 * @return mixed */ public static function invokeMethod($method, $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); } $args = self::bindParams($reflect, $vars); // 記錄執行信息 self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); return $reflect->invokeArgs(isset($class) ? $class : null, $args); } /** * 綁定參數 * @access public * @param \ReflectionMethod|\ReflectionFunction $reflect 反射類 * @param array $vars 變量 * @return array */ private static function bindParams($reflect, $vars = []) { if (empty($vars)) { // 自動獲取請求變量 if (Config::get('url_param_type')) { $vars = Request::instance()->route(); } else { $vars = Request::instance()->param(); } } $args = []; // 判斷數組類型 數字數組時按順序綁定參數 reset($vars); $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) { $className = $class->getName(); if (isset($vars[$name]) && $vars[$name] instanceof $className) { $args[] = $vars[$name]; unset($vars[$name]); } else { $args[] = method_exists($className, 'instance') ? $className::instance() : new $className(); } } 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 { throw new \InvalidArgumentException('method param miss:' . $name); } } // 全局過濾 array_walk_recursive($args, [Request::instance(), 'filterExp']); } return $args; } /** * 執行模塊 * @access public * @param array $result 模塊/控制器/操作 * @param array $config 配置參數 * @param bool $convert 是否自動轉換控制器和操作名 * @return mixed */ public static function module($result, $config, $convert = null) {// 其實就對 模型 路徑的一個解析 if (is_string($result)) { $result = explode('/', $result); } $request = Request::instance(); if ($config['app_multi_module']) { // 多模塊部署 $module = strip_tags(strtolower($result[0] ?: $config['default_module'])); $bind = Route::getBind('module'); $available = false; if ($bind) { // 綁定模塊 list($bindModule) = explode('/', $bind); if (empty($result[0])) { $module = $bindModule; $available = true; } elseif ($module == $bindModule) { $available = true; } } elseif (!in_array($module, $config['deny_module_list']) && is_dir(APP_PATH . $module)) { $available = true; } // 模塊初始化 if ($module && $available) { // 初始化模塊 $request->module($module); $config = self::init($module); } else { throw new HttpException(404, 'module not exists:' . $module); } } else { // 單一模塊部署 $module = ''; $request->module($module); } // 當前模塊路徑 App::$modulePath = APP_PATH . ($module ? $module . DS : ''); // 是否自動轉換控制器和操作名 $convert = is_bool($convert) ? $convert : $config['url_convert']; // 獲取控制器名 $controller = strip_tags($result[1] ?: $config['default_controller']); $controller = $convert ? strtolower($controller) : $controller; // 獲取操作名 $actionName = strip_tags($result[2] ?: $config['default_action']); $actionName = $convert ? strtolower($actionName) : $actionName; // 設置當前請求的控制器、操作 $request->controller(Loader::parseName($controller, 1))->action($actionName); // 監聽module_init Hook::listen('module_init', $request); try { $instance = Loader::controller($controller, $config['url_controller_layer'], $config['controller_suffix'], $config['empty_controller']); if (is_null($instance)) { throw new HttpException(404, 'controller not exists:' . Loader::parseName($controller, 1)); } // 獲取當前操作名 $action = $actionName . $config['action_suffix']; if (!preg_match('/^[A-Za-z](\w)*$/', $action)) { // 非法操作 throw new \ReflectionException('illegal action name:' . $actionName); } // 執行操作方法 $call = [$instance, $action]; Hook::listen('action_begin', $call); $data = self::invokeMethod($call); } catch (\ReflectionException $e) { // 操作不存在 if (method_exists($instance, '_empty')) { $reflect = new \ReflectionMethod($instance, '_empty'); $data = $reflect->invokeArgs($instance, [$action]); self::$debug && Log::record('[ RUN ] ' . $reflect->__toString(), 'info'); } else { throw new HttpException(404, 'method not exists:' . (new \ReflectionClass($instance))->getName() . '->' . $action); } } return $data; } /** * 初始化應用 */ public static function initCommon() { if (empty(self::$init)) {// 初始化 配置 選項 // 初始化應用 $config = self::init(); self::$suffix = $config['class_suffix'];// 此處 默認是 false 方式。 // 應用調試模式 self::$debug = Env::get('app_debug', Config::get('app_debug')); if (!self::$debug) {// 如果 非 系統 顯示 ini_set('display_errors', 'Off');// } elseif (!IS_CLI) {// 非命令行 模式 //重新申請一塊比較大的buffer if (ob_get_level() > 0) { $output = ob_get_clean(); } ob_start();// 默認開始 緩存輸出 if (!empty($output)) { echo $output; } } // 註冊應用命名空間 self::$namespace = $config['app_namespace']; Loader::addNamespace($config['app_namespace'], APP_PATH); if (!empty($config['root_namespace'])) {// 默認為空 Loader::addNamespace($config['root_namespace']); } // 加載額外文件 if (!empty($config['extra_file_list'])) {// 加載幫助文件 //'extra_file_list' => [THINK_PATH . 'helper' . EXT], foreach ($config['extra_file_list'] as $file) { $file = strpos($file, '.') ? $file : APP_PATH . $file . EXT; if (is_file($file) && !isset(self::$file[$file])) { include $file; self::$file[$file] = true;// 並且 提示文件已經 加載完成瞭 } } } // 設置系統時區 date_default_timezone_set($config['default_timezone']); // 監聽app_init Hook::listen('app_init'); self::$init = $config; } return self::$init;// 返回 他【它】 } /** * 初始化應用或模塊 * @access public * @param string $module 模塊名 * @return array */ private static function init($module = '') { // 定位模塊目錄 $module = $module ? $module . DS : '';// 默認為空 // 加載初始化文件 if (is_file(APP_PATH . $module . 'init' . EXT)) {// 如果存在 模塊 初始化 文件 include APP_PATH . $module . 'init' . EXT; } elseif (is_file(RUNTIME_PATH . $module . 'init' . EXT)) {// 如果存在 運行編譯後的,初始化模塊 include RUNTIME_PATH . $module . 'init' . EXT; } else {// 進行默認 項目的加載 $path = APP_PATH . $module; // 加載模塊配置 $config = Config::load(CONF_PATH . $module . 'config' . CONF_EXT);// 默認加載 application/config.php 文件 // 讀取擴展配置文件 if ($config['extra_config_list']) { // 默認配置為 'extra_config_list' => ['database', 'validate'], foreach ($config['extra_config_list'] as $name => $file) { $filename = CONF_PATH . $module . $file . CONF_EXT; Config::load($filename, is_string($name) ? $name : pathinfo($filename, PATHINFO_FILENAME)); } } // 加載應用狀態配置 if ($config['app_status']) { $config = Config::load(CONF_PATH . $module . $config['app_status'] . CONF_EXT); } // 加載行為擴展文件 if (is_file(CONF_PATH . $module . 'tags' . EXT)) { Hook::import(include CONF_PATH . $module . 'tags' . EXT); } // 加載公共文件 if (is_file($path . 'common' . EXT)) { include $path . 'common' . EXT; } // 加載當前模塊語言包 if ($config['lang_switch_on'] && $module) { Lang::load($path . 'lang' . DS . Request::instance()->langset() . EXT); }// 語言包 加載 默認為空 } return Config::get();// 返回 Config::$config['_sys_']// 內容, // 以及對應的 普通 文件包含 } /** * URL路由檢測(根據PATH_INFO) * @access public * @param \think\Request $request * @param array $config * @return array * @throws \think\Exception */ public static function routeCheck($request, array $config) { $path = $request->path(); $depr = $config['pathinfo_depr']; $result = false; // 路由檢測 $check = !is_null(self::$routeCheck) ? self::$routeCheck : $config['url_route_on']; if ($check) { // 開啟路由 if (is_file(RUNTIME_PATH . 'route.php')) { // 讀取路由緩存 $rules = include RUNTIME_PATH . 'route.php'; if (is_array($rules)) { Route::rules($rules); } } else { $files = $config['route_config_file']; foreach ($files as $file) { if (is_file(CONF_PATH . $file . CONF_EXT)) { // 導入路由配置 $rules = include CONF_PATH . $file . CONF_EXT; if (is_array($rules)) { Route::import($rules); } } } } // 路由檢測(根據路由定義返回不同的URL調度) $result = Route::check($request, $path, $depr, $config['url_domain_deploy']); $must = !is_null(self::$routeMust) ? self::$routeMust : $config['url_route_must']; if ($must && false === $result) { // 路由無效 throw new HttpException(404, 'Route Not Found'); } } if (false === $result) { // 路由無效 解析模塊/控制器/操作/參數... 支持控制器自動搜索 $result = Route::parseUrl($path, $depr, $config['controller_auto_search']); } return $result; } /** * 設置應用的路由檢測機制 * @access public * @param bool $route 是否需要檢測路由 * @param bool $must 是否強制檢測路由 * @return void */ public static function route($route, $must = false) { self::$routeCheck = $route; self::$routeMust = $must; } }