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

特别说明一下: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请求产生了。

猜你喜欢

转载自blog.csdn.net/tanmx219/article/details/78939633