摘要:根據提供的超級全局數組來創建實例上面的代碼有一處需要額外解釋一下,自開始內建的可以通過命令行解釋器來啟動,例如但是內建有一個是將和這兩個請求首部存儲到了和中,為了統一內建服務器和真正的中的請求首部字段所以在這里做了特殊處理。
Request
很多框架都會將來自客戶端的請求抽象成類方便應用程序使用,在Laravel中也不例外。IlluminateHttpRequest類在Laravel框架中就是對客戶端請求的抽象,它是構建在Symfony框架提供的Request組件基礎之上的。今天這篇文章就簡單來看看Laravel是怎么創建請求Request對象的,而關于Request對象為應用提供的能力我并不會過多去說,在我講完創建過程后你也就知道去源碼哪里找Request對象提供的方法了,網上有些速查表列舉了一些Request提供的方法不過不夠全并且有的也沒有解釋,所以我還是推薦在開發中如果好奇Request是否已經實現了你想要的能力時去Request的源碼里看下有沒有提供對應的方法,方法注釋里都清楚地標明了每個方法的執行結果。下面讓我們進入正題吧。
創建Request對象我們可以在Laravel應用程序的index.php文件中看到,在Laravel應用程序正式啟動完成前Request對象就已經被創建好了:
//public/index.php $app = require_once __DIR__."/../bootstrap/app.php"; $kernel = $app->make(IlluminateContractsHttpKernel::class); $response = $kernel->handle( //創建request對象 $request = IlluminateHttpRequest::capture() );
客戶端的HTTP請求是IlluminateHttpRequest類的對象
class Request extends SymfonyRequest implements Arrayable, ArrayAccess { //新建Request實例 public static function capture() { static::enableHttpMethodParameterOverride(); return static::createFromBase(SymfonyRequest::createFromGlobals()); } }
通過IlluminateHttpRequest類的源碼可以看到它是繼承自Symfony Request類的,所以IlluminateHttpRequest類中實現的很多功能都是以Symfony Reques提供的功能為基礎來實現的。上面的代碼就可以看到capture方法新建Request對象時也是依賴于Symfony Request類的實例的。
namespace SymfonyComponentHttpFoundation; class Request { /** * 根據PHP提供的超級全局數組來創建Smyfony Request實例 * * @return static */ public static function createFromGlobals() { // With the php"s bug #66606, the php"s built-in web server // stores the Content-Type and Content-Length header values in // HTTP_CONTENT_TYPE and HTTP_CONTENT_LENGTH fields. $server = $_SERVER; if ("cli-server" === PHP_SAPI) { if (array_key_exists("HTTP_CONTENT_LENGTH", $_SERVER)) { $server["CONTENT_LENGTH"] = $_SERVER["HTTP_CONTENT_LENGTH"]; } if (array_key_exists("HTTP_CONTENT_TYPE", $_SERVER)) { $server["CONTENT_TYPE"] = $_SERVER["HTTP_CONTENT_TYPE"]; } } $request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server); if (0 === strpos($request->headers->get("CONTENT_TYPE"), "application/x-www-form-urlencoded") && in_array(strtoupper($request->server->get("REQUEST_METHOD", "GET")), array("PUT", "DELETE", "PATCH")) ) { parse_str($request->getContent(), $data); $request->request = new ParameterBag($data); } return $request; } }
上面的代碼有一處需要額外解釋一下,自PHP5.4開始PHP內建的builtin web server可以通過命令行解釋器來啟動,例如:
php -S localhost:8000 -t htdocs
-S: Run with built-in web server. -t Specify document root for built-in web server.
但是內建web server有一個bug是將CONTENT_LENGTH和CONTENT_TYPE這兩個請求首部存儲到了HTTP_CONTENT_LENGTH和HTTP_CONTENT_TYPE中,為了統一內建服務器和真正的server中的請求首部字段所以在這里做了特殊處理。
Symfony Request 實例的創建是通過PHP中的超級全局數組來創建的,這些超級全局數組有$_GET,$_POST,$_COOKIE,$_FILES,$_SERVER涵蓋了PHP中所有與HTTP請求相關的超級全局數組,創建Symfony Request實例時會根據這些全局數組創建Symfony Package里提供的ParamterBag ServerBag FileBag HeaderBag實例,這些Bag都是Symfony提供地針對不同HTTP組成部分的訪問和設置API, 關于Symfony提供的ParamterBag這些實例有興趣的讀者自己去源碼里看看吧,這里就不多說了。
class Request { /** * @param array $query The GET parameters * @param array $request The POST parameters * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...) * @param array $cookies The COOKIE parameters * @param array $files The FILES parameters * @param array $server The SERVER parameters * @param string|resource|null $content The raw body data */ public function __construct(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) { $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content); } public function initialize(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) { $this->request = new ParameterBag($request); $this->query = new ParameterBag($query); $this->attributes = new ParameterBag($attributes); $this->cookies = new ParameterBag($cookies); $this->files = new FileBag($files); $this->server = new ServerBag($server); $this->headers = new HeaderBag($this->server->getHeaders()); $this->content = $content; $this->languages = null; $this->charsets = null; $this->encodings = null; $this->acceptableContentTypes = null; $this->pathInfo = null; $this->requestUri = null; $this->baseUrl = null; $this->basePath = null; $this->method = null; $this->format = null; } }
可以看到Symfony Request類除了上邊說到的那幾個,還有很多屬性,這些屬性在一起構成了對HTTP請求完整的抽象,我們可以通過實例屬性方便地訪問Method,Charset等這些HTTP請求的屬性。
拿到Symfony Request實例后, Laravel會克隆這個實例并重設其中的一些屬性:
namespace IlluminateHttp; class Request extends .... { //在Symfony request instance的基礎上創建Request實例 public static function createFromBase(SymfonyRequest $request) { if ($request instanceof static) { return $request; } $content = $request->content; $request = (new static)->duplicate( $request->query->all(), $request->request->all(), $request->attributes->all(), $request->cookies->all(), $request->files->all(), $request->server->all() ); $request->content = $content; $request->request = $request->getInputSource(); return $request; } public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) { return parent::duplicate($query, $request, $attributes, $cookies, $this->filterFiles($files), $server); } } //Symfony Request中的 duplicate方法 public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null) { $dup = clone $this; if (null !== $query) { $dup->query = new ParameterBag($query); } if (null !== $request) { $dup->request = new ParameterBag($request); } if (null !== $attributes) { $dup->attributes = new ParameterBag($attributes); } if (null !== $cookies) { $dup->cookies = new ParameterBag($cookies); } if (null !== $files) { $dup->files = new FileBag($files); } if (null !== $server) { $dup->server = new ServerBag($server); $dup->headers = new HeaderBag($dup->server->getHeaders()); } $dup->languages = null; $dup->charsets = null; $dup->encodings = null; $dup->acceptableContentTypes = null; $dup->pathInfo = null; $dup->requestUri = null; $dup->baseUrl = null; $dup->basePath = null; $dup->method = null; $dup->format = null; if (!$dup->get("_format") && $this->get("_format")) { $dup->attributes->set("_format", $this->get("_format")); } if (!$dup->getRequestFormat(null)) { $dup->setRequestFormat($this->getRequestFormat(null)); } return $dup; }
Request對象創建好后在Laravel應用中我們就能方便的應用它提供的能力了,在使用Request對象時如果你不知道它是否實現了你想要的功能,很簡單直接去IlluminateHttpRequest的源碼文件里查看就好了,所有方法都列在了這個源碼文件里,比如:
/** * Get the full URL for the request. * 獲取請求的URL(包含host, 不包括query string) * * @return string */ public function fullUrl() { $query = $this->getQueryString(); $question = $this->getBaseUrl().$this->getPathInfo() == "/" ? "/?" : "?"; return $query ? $this->url().$question.$query : $this->url(); } /** * Get the full URL for the request with the added query string parameters. * 獲取包括了query string 的完整URL * * @param array $query * @return string */ public function fullUrlWithQuery(array $query) { $question = $this->getBaseUrl().$this->getPathInfo() == "/" ? "/?" : "?"; return count($this->query()) > 0 ? $this->url().$question.http_build_query(array_merge($this->query(), $query)) : $this->fullUrl().$question.http_build_query($query); }Request經過的驛站
創建完Request對象后, Laravel的Http Kernel會接著往下執行:加載服務提供器引導Laravel應用、啟動應用、讓Request經過基礎的中間件、通過Router匹配查找Request對應的路由、執行匹配到的路由、Request經過路由上到中間件到達控制器方法。
總結隨著Request最終到達對應的控制器方法后它的使命基本上也就完成了, 在控制器方法里從Request中獲取輸入參數然后執行應用的某一業務邏輯獲得結果,結果會被轉化成Response響應對象返回給發起請求的客戶端。
這篇文章主要梳理了Laravel中Request對象,主要是想讓大家知道如何去查找Laravel中Request現有提供了哪些能力供我們使用避免我們在業務代碼里重新造輪子去實現Request已經提供的方法。
本文已經收錄在系列文章Laravel源碼學習里,歡迎訪問閱讀。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/30804.html
摘要:設置生成對象后就要執行對象的方法了,該方法定義在類中,其主要目的是對進行微調使其能夠遵從協議。最后會把完整的響應發送給客戶端。本文已經收錄在系列文章源碼學習里,歡迎訪問閱讀。 Response 前面兩節我們分別講了Laravel的控制器和Request對象,在講Request對象的那一節我們看了Request對象是如何被創建出來的以及它支持的方法都定義在哪里,講控制器時我們詳細地描述了...
摘要:下面是剛才說的這些步驟對應的核心代碼收集路由和控制器里應用的中間件我們在前面的文章里已經詳細的解釋過中間件和路由的原理了,接下來就看看當請求最終找到了路由對應的控制器方法后是如何為控制器方法注入正確的參數并調用控制器方法的。 控制器 控制器能夠將相關的請求處理邏輯組成一個單獨的類, 通過前面的路由和中間件兩個章節我們多次強調Laravel應用的請求在進入應用后首現會通過Http Ker...
摘要:通過裝載看守器和用戶提供器裝載看守器和用戶提供器用到的方法比較多,用文字描述不太清楚,我們通過注解這個過程中用到的方法來看具體的實現細節。 用戶認證系統的實現細節 上一節我們介紹來Laravel Auth系統的基礎知識,說了他的核心組件都有哪些構成,這一節我們會專注Laravel Auth系統的實現細節,主要關注Auth也就是AuthManager是如何裝載認證用的看守器(Guard)...
摘要:解析出后將進入應用的請求對象傳遞給的方法,在方法負責處理流入應用的請求對象并返回響應對象。攜帶了本次迭代的值。通過這種方式讓請求對象依次流過了要通過的中間件,達到目的地的方法。 中間件(Middleware)在Laravel中起著過濾進入應用的HTTP請求對象(Request)和完善離開應用的HTTP響應對象(Reponse)的作用, 而且可以通過應用多個中間件來層層過濾請求、逐步完善...
摘要:請求未通過的驗證時會拋出此異常。異常處理是非常重要但又容易讓開發者忽略的功能,這篇文章簡單解釋了內部異常處理的機制以及擴展異常處理的方式方法。 異常處理是編程中十分重要但也最容易被人忽視的語言特性,它為開發者提供了處理程序運行時錯誤的機制,對于程序設計來說正確的異常處理能夠防止泄露程序自身細節給用戶,給開發者提供完整的錯誤回溯堆棧,同時也能提高程序的健壯性。 這篇文章我們來簡單梳理一下...
閱讀 2550·2021-10-11 10:58
閱讀 1031·2019-08-29 13:58
閱讀 1670·2019-08-26 13:32
閱讀 835·2019-08-26 10:40
閱讀 3262·2019-08-26 10:18
閱讀 1761·2019-08-23 14:18
閱讀 1111·2019-08-23 10:54
閱讀 441·2019-08-22 18:39