Request

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

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源码学习里,欢迎访问阅读。

Laravel核心解读 -- Request相关推荐

  1. Laravel核心解读--服务容器(IocContainer)

    Laravel的核心是IocContainer, 文档中称其为"服务容器",服务容器是一个用于管理类依赖和执行依赖注入的强大工具,Laravel中的功能模块比如 Route.Elo ...

  2. Laravel核心解读--完结篇

    过去一年时间写了20多篇文章来探讨了我认为的Larave框架最核心部分的设计思路.代码实现.通过更新文章自己在软件设计.文字表达方面都有所提高,在刚开始决定写Laravel源码分析地文章的时候我地期望 ...

  3. Laravel核心解读--完结篇 1

    过去一年时间写了20多篇文章来探讨了我认为的Larave框架最核心部分的设计思路.代码实现.通过更新文章自己在软件设计.文字表达方面都有所提高,在刚开始决定写Laravel源码分析地文章的时候我地期望 ...

  4. Laravel核心解读--控制器

    控制器 控制器能够将相关的请求处理逻辑组成一个单独的类, 通过前面的路由和中间件两个章节我们多次强调Laravel应用的请求在进入应用后首现会通过Http Kernel里定义的基本中间件 protec ...

  5. Laravel核心解读 -- 用户认证系统(基础介绍)

    用户认证系统(基础介绍) 使用过Laravel的开发者都知道,Laravel自带了一个认证系统来提供基本的用户注册.登录.认证.找回密码,如果Auth系统里提供的基础功能不满足需求还可以很方便的在这些 ...

  6. Laravel核心解读--控制器 1

    控制器 控制器能够将相关的请求处理逻辑组成一个单独的类, 通过前面的路由和中间件两个章节我们多次强调Laravel应用的请求在进入应用后首现会通过Http Kernel里定义的基本中间件 protec ...

  7. Laravel核心解读--Facades

    什么是Facades Facades是我们在Laravel应用开发中使用频率很高的一个组件,叫组件不太合适,其实它们是一组静态类接口或者说代理,让开发者能简单的访问绑定到服务容器里的各种服务.Lara ...

  8. Laravel核心解读--中间件(Middleware)

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

  9. Laravel核心解读--服务提供器(ServiceProvider)

    服务提供器是所有 Laravel 应用程序引导中心.你的应用程序自定义的服务.第三方资源包提供的服务以及 Laravel 的所有核心服务都是通过服务提供器进行注册(register)和引导(boot) ...

最新文章

  1. 【常见CPU架构对比】维基百科
  2. Spring源码窥探之:xxxAware接口
  3. 一个免费的页面素材网站
  4. 更改应用程序图标_基于安卓11的ColorOS 11的主要功能和更改日志
  5. object detection错误Message type object_detection.protos.SsdFeatureExtractor has no field named bat
  6. 【前端】【thymeleaf】thymeleaf初始化的表格循环
  7. 让人死去活来的cocos2d-x安卓开发环境搭建(windows+eclipse+ndk 不用cygwin)【上图】
  8. dns服务器配置错误无法修复,电脑DNS错误修复的方法教程
  9. mysql 前几个月的时间_MYsql 查询 查询当前周、月份及前几个月的数据(时间 查询)...
  10. 复制mathtype到Word时
  11. Ubuntu开机自启动程序的方法
  12. 辨析 dB、dBm、dBw
  13. VUE之组件(Props特性深析)
  14. inet_csk_get_port(...)
  15. mAP的计算公式是什么?
  16. Clamav杀毒软件源码分析笔记 六
  17. Shell脚本:一键优化系统脚本
  18. python指定时间倒计时软件_python倒计时
  19. 微信小程序(day04)
  20. 用python画六瓣雪花剪纸折法_窗花的剪制方法(六瓣花、雪花) 来充电吧

热门文章

  1. 《大数据、小数据、无数据:网络世界的数据学术》一 3.5 交流融合
  2. PHP生成随机密码的4种方法及性能对比
  3. java类与继承的执行顺序
  4. python import 错误 TypeError: 'module' object is not callable
  5. 皕杰报表和炎黄盈动(AWS BPM)集成 操作手册
  6. 适用于各种连锁企业15寸多点触摸android收款机消费机pos机
  7. ASP.NET 动态加载WebService功能
  8. Asp.Net MVC使用HtmlHelper渲染,并传递FormCollection参数的陷阱
  9. 2008哲思自由软件峰会
  10. 移动端采用Flexible将PX转换REM适配及开发中Retina屏1px边框的两种解决方案