Laravel5.5源碼詳解 – Request是如何產生的。
在laravel的啟動頁面,也就是public/index.php檔案內,有這麼一句,
$response = $kernel->handle( $request = Illuminate\Http\Request::capture());
它根據瀏覽器傳入的HTTP請求,建立了一個Illuminate\Http\Request執行個體。下面,我們看一下這個執行個體的產生過程。
$request Illuminate\Http\Request::capture()
public static function capture(){ static::enableHttpMethodParameterOverride(); return static::createFromBase(SymfonyRequest::createFromGlobals());}
這裡面涉及到三個函數, A. Request::capture()調用的第1個和第2個函數
建立Request的時候,調用的相關函數如下
Vendor\Symfony\Http-foundation\Request
public static function enableHttpMethodParameterOverride(){ self::$httpMethodParameterOverride = true;}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. // 上面官方的注釋已經說明,應該有HTTP_CONTENT_TYPE 和 HTTP_CONTENT_LENGTH,但調試發現都沒有。 $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執行個體 $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;}private static function createRequestFromFactory(array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null){ if (self::$requestFactory) { $request = call_user_func(self::$requestFactory, $query, $request, $attributes, $cookies, $files, $server, $content); if (!$request instanceof self) { throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.'); } return $request; } return new static($query, $request, $attributes, $cookies, $files, $server, $content);}
最後這一句 return new static($query, $request, $attributes, $cookies, $files, $server, $content);,產生了Request的執行個體,即Symfony\Component\HttpFoundation\Request。
注意,Symfony\Http-foundation\Request的namespace是Symfony\Component\HttpFoundation; 說明一: a) $_SERVER是一個數組,它記錄了服務端的資訊,這個在伺服器啟動時生效,
array:24 [▼ "DOCUMENT_ROOT" => "D:\wamp64\www\laravel\laraveltest\public" "REMOTE_ADDR" => "127.0.0.1" "REMOTE_PORT" => "12492" "SERVER_SOFTWARE" => "PHP 7.1.9 Development Server" "SERVER_PROTOCOL" => "HTTP/1.1" "SERVER_NAME" => "127.0.0.1" "SERVER_PORT" => "8000" "REQUEST_URI" => "/user/image/avatarUpload" "REQUEST_METHOD" => "GET" "SCRIPT_NAME" => "/index.php" "SCRIPT_FILENAME" => "D:\wamp64\www\laravel\laraveltest\public\index.php" "PATH_INFO" => "/user/image/avatarUpload" "PHP_SELF" => "/index.php/user/image/avatarUpload" "HTTP_HOST" => "localhost:8000" "HTTP_CONNECTION" => "keep-alive" "HTTP_CACHE_CONTROL" => "max-age=0" "HTTP_USER_AGENT" => "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36" "HTTP_UPGRADE_INSECURE_REQUESTS" => "1" "HTTP_ACCEPT" => "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8" "HTTP_ACCEPT_ENCODING" => "gzip, deflate, br" "HTTP_ACCEPT_LANGUAGE" => "zh-CN,zh;q=0.9" "HTTP_COOKIE" => "remember_web_59ba36addc2b2f9401580f014c7f58ea4e30989d=eyJpdiI6ImdJRXdTZlNBbEt3a1pkM3p0OWxMTVE9PSIsInZhbHVlIjoiejZGdnZ2QlFDY0djQUN5bXd5eGpUVHgyZFUzTkV5ZTZqRVJCVE " "REQUEST_TIME_FLOAT" => 1514626260.1018 "REQUEST_TIME" => 1514626260]
b) PHP_SAPI = ‘cli-server’,
參考, http://php.net/manual/zh/features.commandline.php
PHP可以應用在終端,也可以應用在Web伺服器中;應用在終端上的SAPI就叫做CLI SAPI,應用在Web伺服器中的就叫做CGI SAPI,在windows下安裝php的話,對應的分別是php.exe和php-cgi.exe。
在PHP中,如何得知自己使用的是哪個 SAPI。
在命令列下,運行 php -v 便能得知該 php 是 CGI 還是 CLI。請參考函數 php_sapi_name() 以及常量 PHP_SAPI。 c) $_COOKIE
特別說明一下:cookie是用戶端儲存資料的一個東西,它的作用是,當使用者登入後,後續的請求不需要再次驗證身份。原因就在於瀏覽器會在客戶的請求的頭部帶上這麼一個cookie值,這個cookie 值標明了本次請求的使用者是誰。當然,使用者不註冊登陸網站,這個cookie也是必須的,只不過裡面沒有身份資訊。
所以,當你開啟瀏覽器,輸入www.mynetwork.com時,瀏覽器會首先發出一個cookie給伺服器mynetwork.com網站的伺服器,告訴伺服器有人來瀏覽頁面了,這時候,伺服器必須響應並處理cookie(即:$_COOKIE)。
另個,各個瀏覽器的初始cookie都是不一樣的,比方說IE11是Null 字元串,Firefox的是下面這個樣子的,
array:2 [▼ "bdshare_firstime" => "1511944517485" "remember_web_59ba36addc2b2f9401580 " => "eyJpdiI6InFBsOUZj "]
d) 其他(傳入createRequestFromFactory的)參數
目前這些參考均為空白的數組[ ],如下
$_GET, $_POST, array(), $_FILES, $server
說明二
關於Cookie管理類。
cookie管理類Illuminate\Cookie\CookieJar是在Illuminate\Cookie\CookieServiceProvider註冊時建立的,
<?phpnamespace Illuminate\Cookie;use Illuminate\Support\ServiceProvider;class CookieServiceProvider extends ServiceProvider{ public function register() { $this->app->singleton('cookie', function ($app) { $config = $app->make('config')->get('session'); return (new CookieJar)->setDefaultPathAndDomain( $config['path'], $config['domain'], $config['secure'], $config['same_site'] ?? null ); }); }}B. Request::capture()調用的第3個
public static function createFromBase(SymfonyRequest $request){ // 首先確認是不是Illuminate\Http\Request執行個體,這裡剛建立的是 // Symfony\Component\HttpFoundation, 所以if判斷為false, if ($request instanceof static) { return $request; } $content = $request->content; // 因為不是Illuminate\Http\Request執行個體,所以這裡建立一個laravel處理的Request類的執行個體 $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; // 有無請求參數,如果有的話,就在這裡拿(剛產生是沒有的,都從$_SERVER等參數那裡拿) $request->request = $request->getInputSource(); return $request;}
這裡很有意思,laravel用了symfony的第三方代碼,直接把Symfony\Component\HttpFoundation對象中的一切參數拿過來,利用$request = (new static)->duplicate(...) 複製的手法,產生了一個Illuminate\Http\Request。
至此,一個完整的Illuminate\Http\Request的執行個體,$request請求產生了。