很多框架都会将来自客户端的请求抽象成类方便应用程序使用,在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类的源码可以看到它是继承自 SymfonyRequest类的,所以 Illuminate\Http\Request类中实现的很多功能都是以 SymfonyReques提供的功能为基础来实现的。上面的代码就可以看到 capture方法新建Request对象时也是依赖于 SymfonyRequest类的实例的。

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.

但是内建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('_format') && $this->get('_format')) {$dup->attributes->set('_format', $this->get('_format'));}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() == '/' ? '/?' : '?';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() == '/' ? '/?' : '?';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已经提供的方法。

Laravel源码解析之Request相关推荐

  1. Laravel源码解析之从入口开始

    前言 提升能力的方法并非使用更多工具,而是解刨自己所使用的工具.今天我们从Laravel启动的第一步开始讲起. 入口文件 laravel是单入口框架,所有请求必将经过index.php define( ...

  2. Laravel源码解析之中间件

    中间件(Middleware)在Laravel中起着过滤进入应用的HTTP请求对象(Request)和完善离开应用的HTTP响应对象(Reponse)的作用, 而且可以通过应用多个中间件来层层过滤请求 ...

  3. Laravel源码解析之Console内核

    上一篇文章我们介绍了Laravel的HTTP内核,详细概述了网络请求从进入应用到应用处理完请求返回HTTP响应整个生命周期中HTTP内核是如何调动Laravel各个核心组件来完成任务的.除了处理HTT ...

  4. Laravel源码解析之ENV配置

    Laravel在启动时会加载项目中的 .env文件.对于应用程序运行的环境来说,不同的环境有不同的配置通常是很有用的. 例如,你可能希望在本地使用测试的 Mysql数据库而在上线后希望项目能够自动切换 ...

  5. Laravel源码解析之事件系统

    Laravel 的事件提供了一个简单的观察者实现,能够订阅和监听应用中发生的各种事件.事件机制是一种很好的应用解耦方式,因为一个事件可以拥有多个互不依赖的监听器. laravel 中事件系统由两部分构 ...

  6. Laravel源码解析之Eloquent Model

    上篇文章我们讲了Database的查询构建器Query Builder, 学习了Query Builder为构建生成SQL语句而提供的Fluent Api的代码实现.这篇文章我们来学习Laravel ...

  7. Laravel源码解析之QueryBuilder

    Database 查询构建器 上文我们说到执行 DB::table('users')->get()是由Connection对象执行table方法返回了一个QueryBuilder对象,Query ...

  8. Laravel源码解析之HTTP Kernel

    由于 PHP 可以处理 WEB 和 CLI 两种接口请求,所以 Laravel中设计 HttpKernel 和 ConsoleKernel 来处理这两种类型的请求,Http Kernel是Larave ...

  9. Laravel源码解析之Response

    之前两篇文章分别讲了Laravel的控制器和Request对象,在讲Request对象的那一节我们看了Request对象是如何被创建出来的以及它支持的方法都定义在哪里,讲控制器时我们详细地描述了如何找 ...

最新文章

  1. antd table设置表格一个单元格的字体颜色_微软Office三件套,各有一个效率神器,全都知道的人不超过1%...
  2. -1.#IND000 图像类型转换
  3. 读书(附电子书)|小狗钱钱之白色的拉布拉多
  4. iOS开发必备指南合集之游戏接入GameCenter 指南
  5. 获取byte的各个bit值_Java中获取一个Byte 的各个Bit的值
  6. salesforce 零基础开发入门学习(十四)salesforce中工厂模式的运用
  7. oracle自定义函数返回一个表,oracle 自定义函数 返回一个表类型
  8. android实现标题栏弹框,Android:Dialog对话框、Builder、showDialog、模板方法设计模式...
  9. 在centos中如何用yum安装最新的yum源
  10. c++ 查看opencv版本 linux
  11. 大众点评字体反爬解析
  12. java seek_java中seek()的用法
  13. 小森生活服务器维护还要多久,小森生活暮夕深林材料刷新时间是多久_暮夕深林材料刷新时间位置汇总_3DM手游...
  14. 快速排序(随机主元)、随机数生成和随机选择算法
  15. Windows下python程序报错Nomodule named 'gevent'解决及ERROR: xxx.whl is not a supported wheel on this platform
  16. 异度装甲解惑(转载)
  17. 【drawio笔记】向ERD表,列表和UML类添加行
  18. Android 进阶——Framework 核心之Android Storage Access Framework(SAF)存储访问框架机制详解(二)
  19. c#winform窗体如何实现数据的保存和读取
  20. 【码上实战】【立体匹配系列】经典SGM:(5)视差优化

热门文章

  1. 在windows下使用Xming+Putty显示Linux下软件图形界面
  2. android获取屏幕宽高与获取控件宽高
  3. 虚拟寄存器,虚拟堆栈与真实寄存器,真实堆栈如何对应
  4. JavaScript跨域问题分析与总结_直来直往_百度空间
  5. 中国股市悬着四把利剑
  6. 参数化查询为什么能够防止SQL注入
  7. Vbs压缩备份文件夹以日期命名
  8. php使用phantomjs
  9. Sqli-labs less 40
  10. “中兴捧月”报文监视器的实现