<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ### **講在前面的話:**? 無數人在使用TP6的過程中,被各種問題所困擾。比如:無法根據錯誤信息快速定位到問題所在,對于手冊模棱兩可的介紹不知所云,對于同事的高級使用技巧不知所措,想擴展框架功能卻不知如何下手等等。究其根本原因就是對框架的實現原理不熟悉,但框架歷經十四年發展,代碼量龐大,紛繁復雜,無數人想深入其中一探究竟,但也只能望洋興嘆,止步不前,苦于無人領路。為了徹底扭轉這一局面,決定編寫和錄制一套全面、系統、深入介紹TP6實現原理的文字課程和視頻課程以幫助大家。 本套課程分為10個章節,分別從生命周期、請求與響應、數據庫、視圖、錯誤和日志、驗證、session和cookie、模型、路由、命令行系統、全面、深入介紹框架實現原理。本章節為生命周期詳解,分為九小節,分別為應用創建、配置加載、服務注冊、事件、加載中間件、中間件執行、路由解析、執行控制器方法、響應輸出,以下本章節詳細內容。 ### 1.1 應用創建: TP6框架采用MVC模型組織項目,采用單一入口(所有請求都是請求同一個文件),由路由模塊解析請求,獲取應用、控制器、方法,接著執行方法,方法返回的內容有響應對象輸出。整個生命周期的輪廓,在入口文件中一覽無余。代碼如下: ``` namespace think; require __DIR__ . '/../vendor/autoload.php'; // 執行HTTP應用并響應 $http = (new App())->http; // 執行應用 $response = $http->run(); // 內容輸出 $response->send(); // 結束應用 $http->end($response); ``` 首先創建 App 對象,此對象為框架中最重要的一個對象,管理后續所創建的系統類對象,相當于對象容器。在創建的過程,會做一些初始化工作,主要是系統目錄的獲取,代碼如下: ``` // vendor\topthink\framework\src\think\App.php 163行 public function __construct(string $rootPath = '') { // 框架核心目錄 src/ $this->thinkPath = dirname(__DIR__) . DIRECTORY_SEPARATOR; // 應用根目錄 比如:frshop/ $this->rootPath = $rootPath ? rtrim($rootPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR : $this->getDefaultRootPath(); $this->appPath = $this->rootPath . 'app' . DIRECTORY_SEPARATOR; $this->runtimePath = $this->rootPath . 'runtime' . DIRECTORY_SEPARATOR; if (is_file($this->appPath . 'provider.php')) { $this->bind(include $this->appPath . 'provider.php'); } static::setInstance($this); $this->instance('app', $this); $this->instance('think\Container', $this); } ``` 接著創建 HTTP 應用,HTTP應用的創建比較特殊,通過獲取 APP 對象不存在屬性,從而觸發 \_\_get() 魔術方法,最終調用 make() 方法來創建對象,并將對象放置于一個容器中,方便后期的獲取,代碼如下: ``` // vendor\topthink\framework\src\think\Container.php 239 行 public function make(string $abstract, array $vars = [], bool $newInstance = false) { $abstract = $this->getAlias($abstract); if (isset($this->instances[$abstract]) && !$newInstance) { return $this->instances[$abstract]; } if (isset($this->bind[$abstract]) && $this->bind[$abstract] instanceof Closure) { $object = $this->invokeFunction($this->bind[$abstract], $vars); } else { $object = $this->invokeClass($abstract, $vars); } if (!$newInstance) { $this->instances[$abstract] = $object; } return $object; } ``` 緊接著執行 HTTP 應用類的 run() 方法啟動一個 HTTP 應用。 ### 1.2 配置加載: 其實,對于任何一個軟件而言,應用執行的第一步都是加載配置,當然TP6也不例外。TP6的配置加載不局限于配置文件的加載,其中包括環境變量加載、助手函數文件加載、配置文件加載、事件文件加載、服務文件加載等等,代碼如下: ``` // vendor\topthink\framework\src\think\App.php 493 行 protected function load(): void { $appPath = $this->getAppPath(); if (is_file($appPath . 'common.php')) { include_once $appPath . 'common.php'; } include_once $this->thinkPath . 'helper.php'; $configPath = $this->getConfigPath(); $files = []; if (is_dir($configPath)) { $files = glob($configPath . '*' . $this->configExt); } foreach ($files as $file) { $this->config->load($file, pathinfo($file, PATHINFO_FILENAME)); } if (is_file($appPath . 'event.php')) { $this->loadEvent(include $appPath . 'event.php'); } if (is_file($appPath . 'service.php')) { $services = include $appPath . 'service.php'; foreach ($services as $service) { $this->register($service); } } } ``` ? 框架配置信息皆由?Config 對象接管,所有配置信息的設置和獲取都通過 Config 對象所提供的的接口設置和獲取,其中重要的接口有 has() 、set() 、 get()。 ### 1.3 服務注冊: 什么是服務?服務就是一個插件,用來擴展內核的功能,分為自定義服務和內置服務,自定義服務可由命令行的 make 命令創建,在通過配置文件配置,就可被框架加載,配置方式如下: ``` // app\service.php use app\AppService; // 系統服務定義文件 // 服務在完成全局初始化之后執行 return [ AppService::class, ]; ``` ?注冊方式如下: ``` // vendor\topthink\framework\src\think\App.php 519行 if (is_file($appPath . 'service.php')) { $services = include $appPath . 'service.php'; foreach ($services as $service) { $this->register($service); } } ``` 系統服務注冊由 RegisterService 類完成,代碼如下: ``` // vendor\topthink\framework\src\think\initializer\RegisterService.php /** * 注冊系統服務 */ class RegisterService { protected $services = [ PaginatorService::class, ValidateService::class, ModelService::class, ]; public function init(App $app) { $file = $app->getRootPath() . 'vendor/services.php'; $services = $this->services; if (is_file($file)) { $services = array_merge($services, include $file); } foreach ($services as $service) { if (class_exists($service)) { $app->register($service); } } } } ``` ### 1.4 事件: 什么是事件?在某個地點,某個時間發生的一件事。純粹討論事件本身并沒有多大意義的,而是事件發生后我們為此做些什么事才是有意義的,這就是事件綁定操作,也就是所謂的事件機制。事件機制能靈活擴展框架的功能,整個事件機制的完成需要三步,一、創建操作(或者叫事件監聽),二、注冊事件監聽,三、觸發事件,代碼如下: 創建事件監聽: ``` // 事件類可以通過命令行快速生成 php think make:event AppInit // 修改 event.php return [ 'bind' => [ ], 'listen' => [ 'AppInit' => ['app\listener\AppInit'], 'HttpRun' => [], 'HttpEnd' => [], 'LogLevel' => [], 'LogWrite' => [], ], 'subscribe' => [ ], ]; ``` 注冊事件監聽(此步驟由系統完成) ``` // vendor\topthink\framework\src\think\Event.php 59 public function listenEvents(array $events) { foreach ($events as $event => $listeners) { if (isset($this->bind[$event])) { $event = $this->bind[$event]; } $this->listener[$event] = array_merge($this->listener[$event] ?? [], $listeners); } return $this; } ``` 觸發事件(此步驟由程序員完成) ``` // 觸發事件 $app->event->trigger(AppInit::class); ``` ### 1.5 加載中間件: ?什么是中間件?顧名思義,就是位于中間的組件,也就是位于生命周期中間的組件,作用就是擴展內核功能。中間件對于框架十分重要,好幾個重要的工功能都是由中間件完成,像 session、請求緩存等等。我們也可以通過中間件來做權限的驗證,不管是自己定義的中間件還是內置的中間件,他們的加載都依賴于配置文件,只有定義在配置文件中的中間件才能加載,代碼如下: ``` // 全局中間件定義文件 app\middleware.php return [ // 全局請求緩存 \think\middleware\CheckRequestCache::class, // 多語言加載 \think\middleware\LoadLangPack::class, // Session初始化 \think\middleware\SessionInit::class ]; ``` 中間價加載,代碼如下: ``` // 加載全局中間件 vendor\topthink\framework\src\think\Http.php 192 行 $this->loadMiddleware(); // loadMiddleware() vendor\topthink\framework\src\think\Http.php 216行 protected function loadMiddleware(): void { if (is_file($this->app->getBasePath() . 'middleware.php')) { $this->app->middleware->import(include $this->app->getBasePath() . 'middleware.php'); } } // import() 方法實現 vendor\topthink\framework\src\think\Middleware.php 51行 public function import(array $middlewares = [], string $type = 'global'): void { foreach ($middlewares as $middleware) { $this->add($middleware, $type); } } ``` 中間價由 Middleware 類接管,中間價存儲在 Middleware 對象的 $queue 屬性中。 ### 1.6 執行中間件: ?中間件的執行應該是整個框架中最核心的地方,也是最難理解的地方,實現的非常巧妙,用文字難以表達清楚,大家可以觀看視頻教程,這里把代碼貼出來。 ``` // 執行應用程序 vendor\topthink\framework\src\think\Http.php 187 行 protected function runWithRequest(Request $request) { $this->initialize(); // 加載全局中間件 $this->loadMiddleware(); // 監聽HttpRun $this->app->event->trigger(HttpRun::class); // 這段代碼涵蓋生命周期的90%,其中包括中間件的執行 return $this->app->middleware->pipeline() ->send($request) ->then(function ($request) { return $this->dispatchToRoute($request); }); } ``` ``` // 調度管道 vendor\topthink\framework\src\think\Middleware.php 133 行 public function pipeline(string $type = 'global') { return (new Pipeline()) ->through(array_map(function ($middleware) { // 這里中間件的執行邏輯,但中間件并沒有在這里執行,只是一個回調函數而已 return function ($request, $next) use ($middleware) { [$call, $params] = $middleware; if (is_array($call) && is_string($call[0])) { $call = [$this->app->make($call[0]), $call[1]]; } $response = call_user_func($call, $request, $next, ...$params); if (!$response instanceof Response) { throw new LogicException('The middleware must return Response instance'); } return $response; }; }, $this->sortMiddleware($this->queue[$type] ?? []))) ->whenException([$this, 'handleException']); } ``` ``` // 執行 vendor\topthink\framework\src\think\Pipeline.php 52 行 // 這段代碼非常的拗口,其難點在于 array_reduce 這個函數,吃透這個函數,就能理解這段代碼 public function then(Closure $destination) { $pipeline = array_reduce( array_reverse($this->pipes), $this->carry(), function ($passable) use ($destination) { try { return $destination($passable); } catch (Throwable | Exception $e) { return $this->handleException($passable, $e); } }); return $pipeline($this->passable); } ``` ### 1.7 路由解析 什么是路由?說穿了路由就是解決從哪里來要到哪里去的問題,框架的最小執行單元是方法,也就是所有的請求最終的落腳點都在方法,但是我們也知道框架是單一入口,所有的請求都是請求同一個文件,那怎么去定位方法呢,這個事由路由完成。路由就是通過解析請求信息,分析出應用->控制器->方法,然后調用方法,并且將方法返回的內容交給響應。核心代碼如下: 路由調度 ``` // 通過此方法 將路由解析 從 http 對象轉給 route 對象 // vendor\topthink\framework\src\think\Http.php 204 行 protected function dispatchToRoute($request) { $withRoute = $this->app->config->get('app.with_route', true) ? function () { $this->loadRoutes(); } : null; return $this->app->route->dispatch($request, $withRoute); } // 路由調度 這是路由解析的核心代碼 // vendor\topthink\framework\src\think\Route.php 739 行 public function dispatch(Request $request, $withRoute = true) { $this->request = $request; $this->host = $this->request->host(true); $this->init(); if ($withRoute) { //加載路由 if ($withRoute instanceof Closure) { $withRoute(); } $dispatch = $this->check(); } else { $dispatch = $this->url($this->path()); } $dispatch->init($this->app); return $this->app->middleware->pipeline('route') ->send($request) ->then(function () use ($dispatch) { return $dispatch->run(); }); } ``` ?解析 url 獲取控制器和方法 ``` // 解析 url vendor\topthink\framework\src\think\route\dispatch\Url.php protected function parseUrl(string $url): array { $depr = $this->rule->config('pathinfo_depr'); $bind = $this->rule->getRouter()->getDomainBind(); if ($bind && preg_match('/^[a-z]/is', $bind)) { $bind = str_replace('/', $depr, $bind); // 如果有域名綁定 $url = $bind . ('.' != substr($bind, -1) ? $depr : '') . ltrim($url, $depr); } $path = $this->rule->parseUrlPath($url); if (empty($path)) { return [null, null]; } // 解析控制器 $controller = !empty($path) ? array_shift($path) : null; if ($controller && !preg_match('/^[A-Za-z0-9][\w|\.]*$/', $controller)) { throw new HttpException(404, 'controller not exists:' . $controller); } // 解析操作 $action = !empty($path) ? array_shift($path) : null; $var = []; // 解析額外參數 if ($path) { preg_replace_callback('/(\w+)\|([^\|]+)/', function ($match) use (&$var) { $var[$match[1]] = strip_tags($match[2]); }, implode('|', $path)); } $panDomain = $this->request->panDomain(); if ($panDomain && $key = array_search('*', $var)) { // 泛域名賦值 $var[$key] = $panDomain; } // 設置當前請求的參數 $this->param = $var; // 封裝路由 $route = [$controller, $action]; if ($this->hasDefinedRoute($route)) { throw new HttpException(404, 'invalid request:' . str_replace('|', $depr, $url)); } return $route; } ``` ### 1.8 執行控制器方法: ? ? ?通過 url 解析之后,我們就可以拿到控制器和方法(如果是多應用,那就是應用->控制器->方法,url 的解析定義在多應用包里面),接下來就是執行方法。代碼如下: ``` // 執行路由調度 vendor\topthink\framework\src\think\route\Dispatch.php public function run(): Response { if ($this->rule instanceof RuleItem && $this->request->method() == 'OPTIONS' && $this->rule->isAutoOptions()) { $rules = $this->rule->getRouter()->getRule($this->rule->getRule()); $allow = []; foreach ($rules as $item) { $allow[] = strtoupper($item->getMethod()); } return Response::create('', 'html', 204)->header(['Allow' => implode(', ', $allow)]); } // 這里是執行控制器方法返回的數據 $data = $this->exec(); return $this->autoResponse($data); } ``` 執行方法 ``` // 執行方法 // vendor\topthink\framework\src\think\route\dispatch\Controller.php 70行 public function exec() { try { // 實例化控制器 $instance = $this->controller($this->controller); } catch (ClassNotFoundException $e) { throw new HttpException(404, 'controller not exists:' . $e->getClass()); } // 注冊控制器中間件 $this->registerControllerMiddleware($instance); return $this->app->middleware->pipeline('controller') ->send($this->request) ->then(function () use ($instance) { // 獲取當前操作名 $suffix = $this->rule->config('action_suffix'); $action = $this->actionName . $suffix; if (is_callable([$instance, $action])) { $vars = $this->request->param(); try { $reflect = new ReflectionMethod($instance, $action); // 嚴格獲取當前操作方法名 $actionName = $reflect->getName(); if ($suffix) { $actionName = substr($actionName, 0, -strlen($suffix)); } $this->request->setAction($actionName); } catch (ReflectionException $e) { $reflect = new ReflectionMethod($instance, '__call'); $vars = [$action, $vars]; $this->request->setAction($action); } } else { // 操作不存在 throw new HttpException(404, 'method not exists:' . get_class($instance) . '->' . $action . '()'); } // 通過反射執行 控制器方法 $data = $this->app->invokeReflectMethod($instance, $reflect, $vars); return $this->autoResponse($data); }); } ``` ### 1.9 響應輸出: 控制器方法執行返回的內容由響應對象接管,響應對象經過一系列處理之后將內容進行輸出,輸出之后響應對象將會關閉請求,但此時應用并沒有結束,而是做一些收尾工作,比如 session 寫入、日志寫入,接下來看看詳細代碼: 接管內容 ``` // 接管內容 根據內容創建不同的響應對象 protected function autoResponse($data): Response { if ($data instanceof Response) { $response = $data; } elseif (!is_null($data)) { // 默認自動識別響應輸出類型 $type = $this->request->isJson() ? 'json' : 'html'; $response = Response::create($data, $type); } else { $data = ob_get_clean(); $content = false === $data ? '' : $data; $status = '' === $content && $this->request->isJson() ? 204 : 200; $response = Response::create($content, 'html', $status); } return $response; } ``` 輸出 ``` // 輸出內容 vendor\topthink\framework\src\think\Response.php 128 public function send(): void { // 處理輸出數據 $data = $this->getContent(); if (!headers_sent() && !empty($this->header)) { // 發送狀態碼 http_response_code($this->code); // 發送頭部信息 foreach ($this->header as $name => $val) { header($name . (!is_null($val) ? ':' . $val : '')); } } if ($this->cookie) { $this->cookie->save(); } $this->sendData($data); // 關閉請求 if (function_exists('fastcgi_finish_request')) { // 提高頁面響應 fastcgi_finish_request(); } } ``` 收尾 ``` // 收尾 vendor\topthink\framework\src\think\Http.php 271 行 public function end(Response $response): void { $this->app->event->trigger(HttpEnd::class, $response); //執行中間件 $this->app->middleware->end($response); // 寫入日志 $this->app->log->save(); } ``` 整個生命周期的介紹到這里就全部結束了,感謝您的閱讀。 讀完此文,如果感覺意猶未盡,看移步觀看視頻教程,并且可以和作者一對一交流。 地址:[https://edu.csdn.net/course/detail/28045](https://edu.csdn.net/course/detail/28045)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看