摘要:根據(jù)提供的超級全局?jǐn)?shù)組來創(chuàng)建實(shí)例上面的代碼有一處需要額外解釋一下,自開始內(nèi)建的可以通過命令行解釋器來啟動,例如但是內(nèi)建有一個(gè)是將和這兩個(gè)請求首部存儲到了和中,為了統(tǒng)一內(nèi)建服務(wù)器和真正的中的請求首部字段所以在這里做了特殊處理。
Request
很多框架都會將來自客戶端的請求抽象成類方便應(yīng)用程序使用,在Laravel中也不例外。IlluminateHttpRequest類在Laravel框架中就是對客戶端請求的抽象,它是構(gòu)建在Symfony框架提供的Request組件基礎(chǔ)之上的。今天這篇文章就簡單來看看Laravel是怎么創(chuàng)建請求Request對象的,而關(guān)于Request對象為應(yīng)用提供的能力我并不會過多去說,在我講完創(chuàng)建過程后你也就知道去源碼哪里找Request對象提供的方法了,網(wǎng)上有些速查表列舉了一些Request提供的方法不過不夠全并且有的也沒有解釋,所以我還是推薦在開發(fā)中如果好奇Request是否已經(jīng)實(shí)現(xiàn)了你想要的能力時(shí)去Request的源碼里看下有沒有提供對應(yīng)的方法,方法注釋里都清楚地標(biāo)明了每個(gè)方法的執(zhí)行結(jié)果。下面讓我們進(jìn)入正題吧。
創(chuàng)建Request對象我們可以在Laravel應(yīng)用程序的index.php文件中看到,在Laravel應(yīng)用程序正式啟動完成前Request對象就已經(jīng)被創(chuàng)建好了:
//public/index.php $app = require_once __DIR__."/../bootstrap/app.php"; $kernel = $app->make(IlluminateContractsHttpKernel::class); $response = $kernel->handle( //創(chuàng)建request對象 $request = IlluminateHttpRequest::capture() );
客戶端的HTTP請求是IlluminateHttpRequest類的對象
class Request extends SymfonyRequest implements Arrayable, ArrayAccess { //新建Request實(shí)例 public static function capture() { static::enableHttpMethodParameterOverride(); return static::createFromBase(SymfonyRequest::createFromGlobals()); } }
通過IlluminateHttpRequest類的源碼可以看到它是繼承自Symfony Request類的,所以IlluminateHttpRequest類中實(shí)現(xiàn)的很多功能都是以Symfony Reques提供的功能為基礎(chǔ)來實(shí)現(xiàn)的。上面的代碼就可以看到capture方法新建Request對象時(shí)也是依賴于Symfony Request類的實(shí)例的。
namespace SymfonyComponentHttpFoundation; class Request { /** * 根據(jù)PHP提供的超級全局?jǐn)?shù)組來創(chuàng)建Smyfony Request實(shí)例 * * @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內(nèi)建的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.
但是內(nèi)建web server有一個(gè)bug是將CONTENT_LENGTH和CONTENT_TYPE這兩個(gè)請求首部存儲到了HTTP_CONTENT_LENGTH和HTTP_CONTENT_TYPE中,為了統(tǒng)一內(nèi)建服務(wù)器和真正的server中的請求首部字段所以在這里做了特殊處理。
Symfony Request 實(shí)例的創(chuàng)建是通過PHP中的超級全局?jǐn)?shù)組來創(chuàng)建的,這些超級全局?jǐn)?shù)組有$_GET,$_POST,$_COOKIE,$_FILES,$_SERVER涵蓋了PHP中所有與HTTP請求相關(guān)的超級全局?jǐn)?shù)組,創(chuàng)建Symfony Request實(shí)例時(shí)會根據(jù)這些全局?jǐn)?shù)組創(chuàng)建Symfony Package里提供的ParamterBag ServerBag FileBag HeaderBag實(shí)例,這些Bag都是Symfony提供地針對不同HTTP組成部分的訪問和設(shè)置API, 關(guān)于Symfony提供的ParamterBag這些實(shí)例有興趣的讀者自己去源碼里看看吧,這里就不多說了。
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類除了上邊說到的那幾個(gè),還有很多屬性,這些屬性在一起構(gòu)成了對HTTP請求完整的抽象,我們可以通過實(shí)例屬性方便地訪問Method,Charset等這些HTTP請求的屬性。
拿到Symfony Request實(shí)例后, Laravel會克隆這個(gè)實(shí)例并重設(shè)其中的一些屬性:
namespace IlluminateHttp; class Request extends .... { //在Symfony request instance的基礎(chǔ)上創(chuàng)建Request實(shí)例 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對象創(chuàng)建好后在Laravel應(yīng)用中我們就能方便的應(yīng)用它提供的能力了,在使用Request對象時(shí)如果你不知道它是否實(shí)現(xiàn)了你想要的功能,很簡單直接去IlluminateHttpRequest的源碼文件里查看就好了,所有方法都列在了這個(gè)源碼文件里,比如:
/** * 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經(jīng)過的驛站
創(chuàng)建完Request對象后, Laravel的Http Kernel會接著往下執(zhí)行:加載服務(wù)提供器引導(dǎo)Laravel應(yīng)用、啟動應(yīng)用、讓Request經(jīng)過基礎(chǔ)的中間件、通過Router匹配查找Request對應(yīng)的路由、執(zhí)行匹配到的路由、Request經(jīng)過路由上到中間件到達(dá)控制器方法。
總結(jié)隨著Request最終到達(dá)對應(yīng)的控制器方法后它的使命基本上也就完成了, 在控制器方法里從Request中獲取輸入?yún)?shù)然后執(zhí)行應(yīng)用的某一業(yè)務(wù)邏輯獲得結(jié)果,結(jié)果會被轉(zhuǎn)化成Response響應(yīng)對象返回給發(fā)起請求的客戶端。
這篇文章主要梳理了Laravel中Request對象,主要是想讓大家知道如何去查找Laravel中Request現(xiàn)有提供了哪些能力供我們使用避免我們在業(yè)務(wù)代碼里重新造輪子去實(shí)現(xiàn)Request已經(jīng)提供的方法。
本文已經(jīng)收錄在系列文章Laravel源碼學(xué)習(xí)里,歡迎訪問閱讀。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/30804.html
摘要:設(shè)置生成對象后就要執(zhí)行對象的方法了,該方法定義在類中,其主要目的是對進(jìn)行微調(diào)使其能夠遵從協(xié)議。最后會把完整的響應(yīng)發(fā)送給客戶端。本文已經(jīng)收錄在系列文章源碼學(xué)習(xí)里,歡迎訪問閱讀。 Response 前面兩節(jié)我們分別講了Laravel的控制器和Request對象,在講Request對象的那一節(jié)我們看了Request對象是如何被創(chuàng)建出來的以及它支持的方法都定義在哪里,講控制器時(shí)我們詳細(xì)地描述了...
摘要:下面是剛才說的這些步驟對應(yīng)的核心代碼收集路由和控制器里應(yīng)用的中間件我們在前面的文章里已經(jīng)詳細(xì)的解釋過中間件和路由的原理了,接下來就看看當(dāng)請求最終找到了路由對應(yīng)的控制器方法后是如何為控制器方法注入正確的參數(shù)并調(diào)用控制器方法的。 控制器 控制器能夠?qū)⑾嚓P(guān)的請求處理邏輯組成一個(gè)單獨(dú)的類, 通過前面的路由和中間件兩個(gè)章節(jié)我們多次強(qiáng)調(diào)Laravel應(yīng)用的請求在進(jìn)入應(yīng)用后首現(xiàn)會通過Http Ker...
摘要:通過裝載看守器和用戶提供器裝載看守器和用戶提供器用到的方法比較多,用文字描述不太清楚,我們通過注解這個(gè)過程中用到的方法來看具體的實(shí)現(xiàn)細(xì)節(jié)。 用戶認(rèn)證系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié) 上一節(jié)我們介紹來Laravel Auth系統(tǒng)的基礎(chǔ)知識,說了他的核心組件都有哪些構(gòu)成,這一節(jié)我們會專注Laravel Auth系統(tǒng)的實(shí)現(xiàn)細(xì)節(jié),主要關(guān)注Auth也就是AuthManager是如何裝載認(rèn)證用的看守器(Guard)...
摘要:解析出后將進(jìn)入應(yīng)用的請求對象傳遞給的方法,在方法負(fù)責(zé)處理流入應(yīng)用的請求對象并返回響應(yīng)對象。攜帶了本次迭代的值。通過這種方式讓請求對象依次流過了要通過的中間件,達(dá)到目的地的方法。 中間件(Middleware)在Laravel中起著過濾進(jìn)入應(yīng)用的HTTP請求對象(Request)和完善離開應(yīng)用的HTTP響應(yīng)對象(Reponse)的作用, 而且可以通過應(yīng)用多個(gè)中間件來層層過濾請求、逐步完善...
摘要:請求未通過的驗(yàn)證時(shí)會拋出此異常。異常處理是非常重要但又容易讓開發(fā)者忽略的功能,這篇文章簡單解釋了內(nèi)部異常處理的機(jī)制以及擴(kuò)展異常處理的方式方法。 異常處理是編程中十分重要但也最容易被人忽視的語言特性,它為開發(fā)者提供了處理程序運(yùn)行時(shí)錯(cuò)誤的機(jī)制,對于程序設(shè)計(jì)來說正確的異常處理能夠防止泄露程序自身細(xì)節(jié)給用戶,給開發(fā)者提供完整的錯(cuò)誤回溯堆棧,同時(shí)也能提高程序的健壯性。 這篇文章我們來簡單梳理一下...
閱讀 2542·2021-10-11 10:58
閱讀 1020·2019-08-29 13:58
閱讀 1661·2019-08-26 13:32
閱讀 830·2019-08-26 10:40
閱讀 3256·2019-08-26 10:18
閱讀 1756·2019-08-23 14:18
閱讀 1106·2019-08-23 10:54
閱讀 435·2019-08-22 18:39