Core interpretation of laravel — request

Time:2021-12-2

Request

Many frameworks Abstract requests from clients into classes to facilitate application use, and laravel is no exception.Illuminate\Http\RequestClass is an abstraction of client requests in the laravel framework. It is built onSymfonyBased on the request component provided by the framework. Today’s article will simply look at how laravel creates the request object. I won’t talk too much about the capabilities provided by the request object for applications. After I finish talking about the creation process, you will know where to find the methods provided by the request object in the source code, Some quick reference tables on the Internet list some methods provided by request, but they are not complete enough and some have not been explained. Therefore, I recommend that if you are curious about whether request has achieved the ability you want, go to the source code of request to see whether corresponding methods are provided. The execution results of each method are clearly indicated in the method comments. Let’s get to the point.

Create request object

We can use it in the laravel applicationindex.phpIt can be seen from the file that the request object has been created before the laravel application is officially started:

//public/index.php
$app = require_once __DIR__.'/../bootstrap/app.php';

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
    //Create request object
    $request = Illuminate\Http\Request::capture()
);

The HTTP request of the client isIlluminate\Http\RequestObject of class

class Request extends SymfonyRequest implements Arrayable, ArrayAccess
{
    //Create a new request instance
    public static function capture()
    {
        static::enableHttpMethodParameterOverride();

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

adoptIlluminate\Http\RequestClass, you can see that it is inherited fromSymfony RequestClass, soIlluminate\Http\RequestMany of the functions implemented in the class are based onSymfony RequesBased on the functions provided. You can see the above codecaptureMethod also depends onSymfony RequestClass.

namespace Symfony\Component\HttpFoundation;
class Request
{
    /**
     *Create the smyfony request instance according to the super global array provided by PHP
     *
     * @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;
    }
    
}

The above code needs an additional explanation. Since PHP 5.4, the built-in builtin web server in PHP can be started through the command-line interpreter, for example:

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.

However, there is a bug in the built-in web serverCONTENT_LENGTHandCONTENT_TYPEThe two requests are stored in the headerHTTP_CONTENT_LENGTHandHTTP_CONTENT_TYPEIn order to unify the request header fields in the built-in server and the real server, special processing is done here.

The symfony request instance is created through super global arrays in PHP. These super global arrays include$_GET$_POST$_COOKIE$_FILES$_SERVERIt covers all super global arrays related to HTTP requests in PHP. When creating a symfony request instance, the global arrays provided in the symfony package will be created according to these global arraysParamterBag ServerBag FileBag HeaderBagFor instance, these bags are the access and setting APIs for different HTTP components provided by symfony. About symfonyParamterBagInterested readers of these examples go to the source code and have a look. I won’t say more here.

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;
    }
    
}

You can see that the symfony request class has many attributes besides those mentioned above. These attributes together constitute a complete abstraction of HTTP requests, which can be accessed easily through instance attributesMethodCharsetAnd so on.

After obtaining the symfony request instance, laravel will clone the instance and reset some of its properties:

namespace Illuminate\Http;
class Request extends ....
{
    //Create a request instance based on the symfony request instance
    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);
    }
}
    //Duplicate method in symfony request
    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;
    }

After the request object is created, we can easily apply the capabilities it provides in the laravel application. When using the request object, if you don’t know whether it realizes the functions you want, you can simply go directlyIlluminate\Http\RequestJust check in the source code file of. All methods are listed in the source code file, such as:

/**
 * Get the full URL for the request.
 *Get the URL of the request (including host, excluding 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.
 *Get the full URL including the query string
 *
 * @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);
}

Post station passed by request

After creating the request object, the HTTP kernel of laravel will continue to execute: load the service provider, guide the laravel application, start the application, let the request pass through the basic middleware, find the route corresponding to the request through the router matching, execute the matched route, and the request passes through the route to the middleware to reach the controller method.

summary

After the request finally reaches the corresponding controller method, its mission is basically completed. In the controller method, obtain the input parameters from the request, and then execute a business logic of the application to obtain the results. The results will be transformed into a response response object and returned to the requesting client.

This article mainly combs the request object in laravel, mainly to let you know how to find out the existing capabilities provided by request in laravel for us to use, so as to avoid us rebuilding the wheel in the business code to implement the methods already provided by request.

This article has been included in a series of articlesLaravel source code learningWelcome to visit and read.

Core interpretation of laravel -- request