搜尋
首頁後端開發php教程Laravel核心解讀Request

Laravel核心解讀Request

Jul 06, 2018 pm 02:54 PM
laravelphp

這篇文章主要介紹了關於Laravel核心解讀Request,有著一定的參考價值,現在分享給大家,有需要的朋友可以參考一下

Request

很多框架都會將來自客戶端的請求抽象化成類別方便應用程式使用,在Laravel中也不例外。 Illuminate\Http\Request類別在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(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    //创建request对象
    $request = Illuminate\Http\Request::capture()
);

客戶端的HTTP請求是Illuminate\Http\Request類別的物件

class Request extends SymfonyRequest implements Arrayable, ArrayAccess
{
    //新建Request实例
    public static function capture()
    {
        static::enableHttpMethodParameterOverride();

        return static::createFromBase(SymfonyRequest::createFromGlobals());
    }
}

透過Illuminate\Http\Request類別的原始碼可以看到它是繼承自Symfony Request類別的,所以Illuminate\Http\Request類別中實作的許多功能都是以Symfony Reques提供的功能為基礎來實現的。上面的程式碼就可以看到capture方法新建Request物件時也是依賴Symfony Request類別的實例的。

namespace Symfony\Component\HttpFoundation;
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 <addr>:<port> Run with built-in web server.
-t <docroot>     Specify document root <docroot> for built-in web server.</docroot></docroot></port></addr>

但是內建web server有一個bug是將CONTENT_LENGTHCONTENT_TYPE這兩個請求首部儲存到了HTTP_CONTENT_LENGTHHTTP_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請求完整的抽象,我們可以透過實例屬性方便地存取MethodCharset等這些HTTP請求的屬性。

拿到Symfony Request實例後, Laravel會複製這個實例並重設其中的一些屬性:

namespace Illuminate\Http;
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(&#39;_format&#39;) && $this->get(&#39;_format&#39;)) {
            $dup->attributes->set(&#39;_format&#39;, $this->get(&#39;_format&#39;));
        }

        if (!$dup->getRequestFormat(null)) {
            $dup->setRequestFormat($this->getRequestFormat(null));
        }

        return $dup;
    }

Request物件建立好後在Laravel應用中我們就能方便的應用它提供的能力了,在使用Request物件時如果你不知道它是否實現了你想要的功能,很簡單直接去Illuminate\Http\Request的源碼檔案裡查看就好了,所有方法都列在了這個原始碼檔案裡,例如:

/**
 * Get the full URL for the request.
 * 获取请求的URL(包含host, 不包括query string)
 *
 * @return string
 */
public function fullUrl()
{
    $query = $this->getQueryString();

    $question = $this->getBaseUrl().$this->getPathInfo() == &#39;/&#39; ? &#39;/?&#39; : &#39;?&#39;;

    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() == &#39;/&#39; ? &#39;/?&#39; : &#39;?&#39;;

    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已經提供的方法。

以上就是本文的全部內容,希望對大家的學習有所幫助,更多相關內容請關注PHP中文網!

相關推薦:

#

以上是Laravel核心解讀Request的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
PHP如何識別用戶的會話?PHP如何識別用戶的會話?May 01, 2025 am 12:23 AM

phpIdentifiesauser'ssessionSessionSessionCookiesAndSessionId.1)whiwsession_start()被稱為,phpgeneratesainiquesesesessionIdStoredInacookInAcookInAcienamedInAcienamedphpsessIdontheuser'sbrowser'sbrowser.2)thisIdallowSphptpptpptpptpptpptpptpptoretoreteretrieetrieetrieetrieetrieetrieetreetrieetrieetrieetrieetremthafromtheserver。

確保PHP會議的一些最佳實踐是什麼?確保PHP會議的一些最佳實踐是什麼?May 01, 2025 am 12:22 AM

PHP會話的安全可以通過以下措施實現:1.使用session_regenerate_id()在用戶登錄或重要操作時重新生成會話ID。 2.通過HTTPS協議加密傳輸會話ID。 3.使用session_save_path()指定安全目錄存儲會話數據,並正確設置權限。

PHP會話文件默認存儲在哪裡?PHP會話文件默認存儲在哪裡?May 01, 2025 am 12:15 AM

phpsessionFilesArestoredIntheDirectorySpecifiedBysession.save_path,通常是/tmponunix-likesystemsorc:\ windows \ windows \ temponwindows.tocustomizethis:tocustomizEthis:1)useession_save_save_save_path_path()

您如何從PHP會話中檢索數據?您如何從PHP會話中檢索數據?May 01, 2025 am 12:11 AM

ToretrievedatafromaPHPsession,startthesessionwithsession_start()andaccessvariablesinthe$_SESSIONarray.Forexample:1)Startthesession:session_start().2)Retrievedata:$username=$_SESSION['username'];echo"Welcome,".$username;.Sessionsareserver-si

您如何使用會議來實施購物車?您如何使用會議來實施購物車?May 01, 2025 am 12:10 AM

利用會話構建高效購物車系統的步驟包括:1)理解會話的定義與作用,會話是服務器端的存儲機制,用於跨請求維護用戶狀態;2)實現基本的會話管理,如添加商品到購物車;3)擴展到高級用法,支持商品數量管理和刪除;4)優化性能和安全性,通過持久化會話數據和使用安全的會話標識符。

您如何在PHP中創建和使用接口?您如何在PHP中創建和使用接口?Apr 30, 2025 pm 03:40 PM

本文解釋瞭如何創建,實施和使用PHP中的接口,重點關注其對代碼組織和可維護性的好處。

crypt()和password_hash()有什麼區別?crypt()和password_hash()有什麼區別?Apr 30, 2025 pm 03:39 PM

本文討論了PHP中的crypt()和password_hash()的差異,以進行密碼哈希,重點介紹其實施,安全性和對現代Web應用程序的適用性。

如何防止PHP中的跨站點腳本(XSS)?如何防止PHP中的跨站點腳本(XSS)?Apr 30, 2025 pm 03:38 PM

文章討論了通過輸入驗證,輸出編碼以及使用OWASP ESAPI和HTML淨化器之類的工具來防止PHP中的跨站點腳本(XSS)。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。