Laravel5.5源码详解 -- Request是如何生成的?
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是空字符串,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注册时创建的,
<?php
namespace 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请求产生了。