摘要:會依據協議,將請求的數據等信息發送給解析器,接下來解析器會解析文件,初始化執行環境,然后處理請求,再以規定的格式返回處理后的結果,退出進程。它的特點是會在動態分配處理進程給請求,以達到提高效率的目的,大多數實現都會維護一個進程池。
PHP作為世界上最好的編程語音,被廣泛的運用到Web開發中。因為其語法和C類似,有著非常平緩的學習曲線,越來越多的人使用PHP進行Web產品的快速開發。PHP世界里也涌現了很多開發框架,比如Laravel、ThinkPHP等,但不論何總框架,他們在處理Web請求時的模式都是一樣的,本文首先闡述PHP開發Web應用的基本架構,然后分別分析Laravel和ThinkPHP在處理Web請求時的處理流程。
PHP開發Web應用的基本架構PHP開發Web應用時所以的請求需要指向具體的入口文件。WebServer是一個內容分發者,他接受用戶的請求后,如果是請求的是css、js等靜態文件,WebServer會找到這個文件,然后發送給瀏覽器;如果請求的是/index.php,根據配置文件,WebServer知道這個不是靜態文件,需要去找PHP解析器來處理,那么他會把這個請求簡單處理后交給PHP解析器。
WebServer會依據CGI協議,將請求的Url、數據、Http Header等信息發送給PHP解析器,接下來PHP解析器會解析php.ini文件,初始化執行環境,然后處理請求,再以CGI規定的格式返回處理后的結果,退出進程。web server再把結果返回給瀏覽器。整個處理過程如上圖所示。
FastCGI這里的PHP解析器就是實現了CGI協議的程序,每次請求到來時他會解析php.ini文件,初始化執行環境,這就導致PHP解析器性能低下,于是就出現了CGI的改良升級版FastCGI。FastCGI是一種語言無關的協議,用來溝通程序(如PHP, Python, Java)和Web服務器(Apache2, Nginx), 理論上任何語言編寫的程序都可以通過FastCGI來提供Web服務。它的特點是會在動態分配處理進程給請求,以達到提高效率的目的,大多數FastCGI實現都會維護一個進程池。FastCGI會先啟一個master進程,解析配置文件,初始化執行環境,然后再啟動多個worker進程。當請求過來時,master進程會這個請求傳遞給一個worker進程,然后立即接受下一個請求。而且當worker進程不夠用時,master可以根據配置預先啟動幾個worker進程等待;當然空閑worker進程太多時,也會自動關閉,這樣就提高了性能,節約了系統資源。整個過程FastCGI扮演著對CGI進程進行管理的角色。
PHP-FPMPHP-FPM是一個專門針對PHP實現了FastCGI協議的程序,它實際上就是一個PHP FastCGI進程管理器,負責管理一個進程池,調用PHP解析器來處理來自Web服務器的請求。PHP-FPM能夠對php.ini文件的修改進行平滑過度。
新建一個helloworld.php文件,寫入下列代碼
配置好WebServer和PHP-FPM等php運行環境后,在瀏覽器中訪問該文件就可以直接得到輸出。
基于PHP的Web框架它主要的任務包括:基于某模式將PHP開發常用功能封裝實現使開發者快速開發的工具
代碼重用:定義包、類、函數的放置和加載規則,建議直接整合Composer及其AutoLoad特性。
請求的分發管理:這個就是路由,Rest風的框架喜歡Rewrite,簡單的一點的框架主要通過參數來定位模塊和方法所在。
配置文件管理:加載和動態加載配置數據
錯誤和異常管理:異常捕捉、錯誤日志記錄以及錯誤碼規范。
Layout和模板引擎:如何規劃頁面布局、widget如何重用、ajax頁面如何結合、過期- session如何重定向;數據和模板怎么渲染成HTML,是否壓縮和設置過期頭。
數據庫:如何融入控制器;支持什么樣的driver;考慮主從分離的擴展性;以及是否使用ORM
ThinkPHP3.2框架處理流程分析
TP的設計邏輯就是簡單粗暴,面對問題解決問題,所以他的處理流程是基于面向過程的思想,而沒有采用面向對象的依賴注入、控制反轉等思路。他的自動加載、錯誤處理通過php原生函數的回調來實現。TP處理每次請求要經過四個步驟如下圖所示:
index.php是TP的入口文件,所有的請求都由該文件接管,它的工作也很簡單主要是引入ThinkPHP入口文件
5.3.0 !"); // 開啟調試模式 建議開發階段開啟 部署階段注釋或者設為false define("APP_DEBUG",False); // 定義應用目錄 define("APP_PATH","./Application/"); // 引入ThinkPHP入口文件 require "./ThinkPHP/ThinkPHP.php";載入框架入口文件ThinkPHP.php
在ThinkPHP.php中主要記錄初始運行時間和內存開銷,然后完成系統常量判斷及定義,最后載入框架引導類(ThinkThink)并執行Think::start方法進行應用初始化。
應用初始化ThinkThink:start()應用初始化首先設置錯誤處理機制和自動加載機制
static public function start() { // 注冊AUTOLOAD方法 spl_autoload_register("ThinkThink::autoload"); // 設定錯誤和異常處理 register_shutdown_function("ThinkThink::fatalError"); set_error_handler("ThinkThink::appError"); set_exception_handler("ThinkThink::appException");
然后加載相關配置文件和運行模式定義文件,最后調用ThinkApp類的run方法啟動應用
運行應用App::run()此后TP進入請求處理管道,TP為管道中定義了14個事件,每個事件都可以綁定回調函數,請求到達管道后依次觸發這些事件,事件觸發后就會調用綁定到事件的回調函數,整個管道的生命周期由app_init開始,由app_end結束。具體實現上,TP將這些事件命名為標簽(位),也可以稱之為鉤子,將回調函數命名為行為,當應用程序運行到標簽的時候,就會被攔截下來,統一執行相關的行為。
Laravel框架處理流程分析統一入口
Laravel框架使用了統一入口,入口文件:/public/index.php
make("IlluminateContractsHttpKernel"); //運行Kernel類的handle方法,主要動作是運行middleware和啟動URL相關的Contrller $response = $kernel->handle( $request = IlluminateHttpRequest::capture() ); //控制器返回結果之后的操作,暫時還沒看,以后補上 $response->send(); $kernel->terminate($request, $response);自動加載文件
laravel的自動加載,其實也就是Composer的自動加載
Composer根據聲明的依賴關系,從相關庫的源下載代碼文件,并根據依賴關系在 Composer 目錄下生成供類自動加載的 PHP 腳本,使用的時候,項目開始處引入 “/vendor/autoload.php” 文件,就可以直接實例化這些第三方類庫中的類了。
服務容器,也叫IoC容器,其實包含了依賴注入(DI)和控制反轉(IoC)兩部分,是Laravel的真正核心。其他的各種功能模塊比如 Route(路由)、Eloquent ORM(數據庫 ORM 組件)、Request and Response(請求和響應)等等等等,實際上都是與核心無關的類模塊提供的,這些類從注冊到實例化,最終被使用,其實都是 Laravel 的服務容器負責的。
啟動Kernel代碼Kernel實例調用handle方法,意味著Laravel的核心和公用代碼已經準備完畢,此項目正式開始運行
代碼清單/app/Http/Kernel.php
"AppHttpMiddlewareAuthenticate", "auth.basic" => "IlluminateAuthMiddlewareAuthenticateWithBasicAuth", "guest" => "AppHttpMiddlewareRedirectIfAuthenticated", "test" => "AppHttpMiddleware estMiddleWare", ]; }
可以看到,其實這個文件里面沒有handle方法,只有一些屬性定義,所以真正的handle方法,實在父類里面實現的
代碼清單…/Illuminate/Foundation/Http/Kernel.php
//這個很重要,是項目的一些啟動引導項,Kernel的重要步驟中,首先就是啟動這些文件的bootstrap方法 protected $bootstrappers = [ //檢測環境變量文件是否正常 "IlluminateFoundationBootstrapDetectEnvironment", //取得配置文件,即把/config/下的所有配置文件讀取到容器(app()->make("config")可以查看所有配置信息) "IlluminateFoundationBootstrapLoadConfiguration", //綁定一個名字為log的實例到容器,怎么訪問??(app()->make("log")) "IlluminateFoundationBootstrapConfigureLogging", //設置異常抓取信息,這個還沒仔細看,但大概就是這個意思 "IlluminateFoundationBootstrapHandleExceptions", //把/config/app.php里面的aliases項利用PHP庫函數class_alias創建別名,從此,我們可以使用App::make("app")方式取得實例 "IlluminateFoundationBootstrapRegisterFacades", //把/config/app.php里面的providers項,注冊到容器 "IlluminateFoundationBootstrapRegisterProviders", //運行容器中注冊的所有的ServiceProvider中得boot方法 "IlluminateFoundationBootstrapBootProviders", ]; //真正的handle方法 public function handle($request) { try { //主要是這行,調度了需要運行的方法 return $this->sendRequestThroughRouter($request); } catch (Exception $e) { $this->reportException($e); return $this->renderException($request, $e); } } protected function sendRequestThroughRouter($request) { $this->app->instance("request", $request); Facade::clearResolvedInstance("request"); //運行上述$bootstrappers里面包含的文件的bootstrap方法,運行的作用,上面已經注釋 $this->bootstrap(); //這是在對URL進行調度之前,也就是運行Route之前,進行的一些準備工作 return (new Pipeline($this->app)) ->send($request) //需要運行$this->middleware里包含的中間件 ->through($this->middleware) //運行完上述中間件之后,調度dispatchToRouter方法,進行Route的操作 ->then($this->dispatchToRouter()); } //前奏執行完畢之后,進行Route操作 protected function dispatchToRouter() { return function($request) { $this->app->instance("request", $request); //跳轉到Router類的dispatch方法 return $this->router->dispatch($request); }; }
下面就需要根據URL和/app/Http/routes.php文件,進行Route操作
文件清單…/Illuminate/Routing/Router.php
public function dispatch(Request $request) { $this->currentRequest = $request; //在4.2版本里面,Route有一個篩選屬性;5.0之后的版本,被Middleware代替 $response = $this->callFilter("before", $request); if (is_null($response)) { //繼續調度 $response = $this->dispatchToRoute($request); } $response = $this->prepareResponse($request, $response); //在4.2版本里面,Route有一個篩選屬性;5.0之后的版本,被Middleware代替 $this->callFilter("after", $request, $response); return $response; } public function dispatchToRoute(Request $request) { $route = $this->findRoute($request); $request->setRouteResolver(function() use ($route) { return $route; }); $this->events->fire("router.matched", [$route, $request]); $response = $this->callRouteBefore($route, $request); if (is_null($response)) { // 只看這一行,還是調度文件 $response = $this->runRouteWithinStack( $route, $request ); } $response = $this->prepareResponse($request, $response); $this->callRouteAfter($route, $request, $response); return $response; } protected function runRouteWithinStack(Route $route, Request $request) { // 取得routes.php里面的Middleware節點 $middleware = $this->gatherRouteMiddlewares($route); //這個有點眼熟 return (new Pipeline($this->container)) ->send($request) //執行上述的中間件 ->through($middleware) ->then(function($request) use ($route) { //到Controller類了 return $this->prepareResponse( $request, //run控制器 $route->run($request) ); }); } public function run(Request $request) { $this->container = $this->container ?: new Container; try { if ( ! is_string($this->action["uses"])) return $this->runCallable($request); if ($this->customDispatcherIsBound()) //實際上是運行了這行 return $this->runWithCustomDispatcher($request); //其實我是直接想運行這行 return $this->runController($request); } catch (HttpResponseException $e) { return $e->getResponse(); } } //繼續調度,最終調度到.../Illuminate/Routing/ControllerDispatcher.php文件的dispatch方法 protected function runWithCustomDispatcher(Request $request) { list($class, $method) = explode("@", $this->action["uses"]); $dispatcher = $this->container->make("illuminate.route.dispatcher"); return $dispatcher->dispatch($this, $request, $class, $method); }
文件清單…/Illuminate/Routing/ControllerDispatcher.php
public function dispatch(Route $route, Request $request, $controller, $method) { $instance = $this->makeController($controller); $this->assignAfter($instance, $route, $request, $method); $response = $this->before($instance, $route, $request, $method); if (is_null($response)) { //還要調度 $response = $this->callWithinStack( $instance, $route, $request, $method ); } return $response; } protected function callWithinStack($instance, $route, $request, $method) { //又是Middleware......有沒有忘記,官方文檔里面Middleware可以加在控制器的構造函數中!!沒錯,這個Middleware就是在控制器里面申明的 $middleware = $this->getMiddleware($instance, $method); //又是這個,眼熟吧 return (new Pipeline($this->container)) ->send($request) //再次運行Middleware ->through($middleware) ->then(function($request) use ($instance, $route, $method) { 運行控制器,返回結果 return $this->call($instance, $route, $method); }); }
終于到達控制器
轉自:http://www.eurekao.com/PHP-pr...
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/22586.html
摘要:的運維追蹤技巧總結曾幾何時我開始運維公司的網站,經過一段時間的摸爬滾打,也算是總結了不少在服務器下調試追蹤各種網站錯誤的方法。 LNMP的運維追蹤技巧總結 曾幾何時我開始運維公司的LNMP網站,經過一段時間的摸爬滾打,也算是總結了不少在LNMP服務器下調試追蹤各種網站錯誤的方法。好記性不如爛筆頭,還是總結一下吧! 在開始我會梳理一下我所理解的一個web請求從發起到響應的各個階段服務器和...
摘要:的運維追蹤技巧總結曾幾何時我開始運維公司的網站,經過一段時間的摸爬滾打,也算是總結了不少在服務器下調試追蹤各種網站錯誤的方法。 LNMP的運維追蹤技巧總結 曾幾何時我開始運維公司的LNMP網站,經過一段時間的摸爬滾打,也算是總結了不少在LNMP服務器下調試追蹤各種網站錯誤的方法。好記性不如爛筆頭,還是總結一下吧! 在開始我會梳理一下我所理解的一個web請求從發起到響應的各個階段服務器和...
摘要:是與之間數據交換的一種協議。當收到這個請求后,會啟動對應的程序,這里就是的解析器。接下來解析器會解析文件,初始化執行環境,然后處理請求,再以規定規定的格式返回處理后的結果,退出進程,再把結果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數據交換的一種協議。FastCGI:同 CGI,是一種通信協議,但比 CGI 在效率上做了一些優化。PHP-...
摘要:是與之間數據交換的一種協議。當收到這個請求后,會啟動對應的程序,這里就是的解析器。接下來解析器會解析文件,初始化執行環境,然后處理請求,再以規定規定的格式返回處理后的結果,退出進程,再把結果返回給瀏覽器。 CGI:是 Web Server 與 Web Application 之間數據交換的一種協議。FastCGI:同 CGI,是一種通信協議,但比 CGI 在效率上做了一些優化。PHP-...
閱讀 4637·2021-10-25 09:48
閱讀 3217·2021-09-07 09:59
閱讀 2198·2021-09-06 15:01
閱讀 2702·2021-09-02 15:21
閱讀 2738·2019-08-30 14:14
閱讀 2192·2019-08-29 13:59
閱讀 2523·2019-08-29 11:02
閱讀 2541·2019-08-26 13:33