生命周期 3--request 对象创建过程浅析
首先,$app对象
和$kernel对象已经创建完毕。
接下来,是创建即将被$kernel对象
处理的$request对象
,然后返回$repsonse
对象.
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
创建$request
对象
-
首先调用Illuminate\Http\Request的capture()方法.
public static function capture() { static::enableHttpMethodParameterOverride(); //step1 return static::createFromBase(SymfonyRequest::createFromGlobals()); //step2-step3 }
step1,设置“可以进行表单方法欺骗”
//如果设置为true,那么对于method为POST的表单,就可以通过_method='PUT'或_method='DELETE',_method='PATCH'来模拟这些类型的请求。
public static function enableHttpMethodParameterOverride()
{
self::$httpMethodParameterOverride = true;
}
- 这也就是所谓的表单方法欺骗。
step2, 调用Symfony框架的Request类的createFromGlobals方法,来创建一个SymfonyRequest对象。
public static function createFromGlobals()
{
$request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER); //step2.1
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'))
) { //step2.2
parse_str($request->getContent(), $data); //step2.2.1-step2.2.2
$request->request = new ParameterBag($data); //step2.2.3
}
return $request;
}
step2.1, 调用Symfony框架的Request类的createRequestFromFactory方法,使用列出的全局变量来创建一个SymfonyRequest对象。
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) { //step2.1.1
$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); //step2.1.2
}
step2.1.1, 判断是否有自定义的request工厂
- 如果有,就调用工厂方法来创建SymfonyRequest对象。
step2.1.2, 如果没有,就new一个SymfonyRequest对象,并将global 变量传进去初始化。
- 初始化SymfonyRequest对象也就是执行initialize方法,用来设置新创建对象的属性。
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); //step2.1.2.1 }
- initialize方法中无非就是各种对global变量的过滤处理,涉及到ParameterBag,FileBag,ServerBag,HeaderBag。
- ParameterBag是一个键值对的容器。
- FileBag是ParameterBag的子类,添加了一些对上传文件特有的操作。
- ServerBag也是ParameterBag的子类,添加了一个getHeaders方法,用来获取HTTP头信息。
- HeaderBag是一个HTTP头信息的容器,里面有关于HTTP头信息的各种方法。
step2.2, 对PUT/DELETE/PATCH路由的处理。
-
如果前面创建的SymfonyRequest对象的headers中CONTENT_TYPE==='application/x-www-form-urlencoded',并且REQUEST_METHOD属于
PUT/DELETE/PATCH
的任意一种,那么就会进行下面的处理。step2.2.1 获取SymfonyRequest对象的body content
$currentContentIsResource = \is_resource($this->content); if (true === $asResource) { //是否需要作为资源返回 if ($currentContentIsResource) { rewind($this->content); return $this->content; } // Content passed in parameter (test) if (\is_string($this->content)) { $resource = fopen('php://temp', 'r+'); fwrite($resource, $this->content); rewind($resource); return $resource; } $this->content = false; return fopen('php://input', 'rb'); } if ($currentContentIsResource) { //如果传入内容是资源,那么将资源转换为字符串返回 rewind($this->content); return stream_get_contents($this->content); } if (null === $this->content || false === $this->content) { //如果content为null或者false,那么从`php://input`去获取字符串。 $this->content = file_get_contents('php://input'); } return $this->content;
$asResource
指定是否要作为资源返回,目前是不需要。那么就是作为字符串返回。- 如果传入内容是资源,那么将资源转换为字符串返回。
- 如果content为null或者false,那么从
php://input
去获取字符串。 - 如果上述条件都没有match,就返回原字符串(一般格式为:avatar_image_id=1&name=hustnzj&email=12345%40qq.com&introduction=hehe)
step2.2.2 将上述body content转换为$data数组
step2.2.3 以$data数组创建一个ParameterBag对象,并赋值给SymfonyRequest对象的request属性。
step3 调用\Illuminate\Http\Request::createFromBase
方法,以SymfonyRequest对象为模板创建一个Illuminate request对象。
public static function createFromBase(SymfonyRequest $request)
{
if ($request instanceof static) { //step3.1
return $request;
}
$content = $request->content; //step3.2
$request = (new static)->duplicate( //step3.3
$request->query->all(), $request->request->all(), $request->attributes->all(),
$request->cookies->all(), $request->files->all(), $request->server->all()
);
$request->content = $content; //step3.4
$request->request = $request->getInputSource(); //step3.5
return $request;
}
step3.1 判断传入的request对象是不是Illuminate request对象
- 如果已经是Illuminate request对象,那么就不用下面的操作了,直接返回。
step3.2 保存SymfonyRequest对象的content到$content变量中备用。
step3.3 new一个IlluminateRequest对象
- 创建IlluminateRequest对象,跟step2.1.2不同的是,没有任何global变量传入,是一个"干净"的对象,
- 将原来SymfonyRequest对象中的各种Bag传入IlluminateRequest对象的duplicate方法,并对files数组进行了空值处理。
- 在
\Symfony\Component\HttpFoundation\Request::duplicate
方法中,对现有IlluminateRequest对象进行了clone,然后将传入的各种bag参数设置到新clone的IlluminateRequest对象上。
step3.4 用step3.2备份的$content重新设置一下当前IlluminateRequest对象的content属性。
step3.5 调用\Illuminate\Http\Request::getInputSource
方法,获取当前request的input source,并设置当前IlluminateRequest对象的request参数包。
protected function getInputSource()
{
if ($this->isJson()) { //step 3.5.1
return $this->json();
}
return in_array($this->getRealMethod(), ['GET', 'HEAD']) ? $this->query : $this->request; //step 3.5.2
}
step3.5.1 判断当前request是否在发送json字符串
public function json($key = null, $default = null)
{
if (! isset($this->json)) {
$this->json = new ParameterBag((array) json_decode($this->getContent(), true));
}
if (is_null($key)) {
return $this->json;
}
return data_get($this->json->all(), $key, $default);
}
- 如果是,就将发送的json字符串打散为数组,然后放在当前request对象的json属性参数包中。
- 如果已经有设置过,就直接返回。
step3.5.2 如果当前request的方法是GET/HEAD就返回query参数包,否则返回request参数包。
- 通过当前request对象的server参数包获取到当前的request_method
public function getRealMethod() { return strtoupper($this->server->get('REQUEST_METHOD', 'GET')); }
综上所述,这个过程就是先启动表单方法重载,然后使用\Symfony\Component\HttpFoundation\Request
的createFromGlobals
方法创建一个SymfonyRequest对象,然后创建一个IlluminateRequest对象,再将SymfonyRequest对象的各种属性值经过各种处理后传过去,最后返回IlluminateRequest对象。
参考
- $_GET, $_POST, array(), $_COOKIE, $_FILES, $_SERVER
- New self vs. new static
$request instanceof static
is false.- clone
- php:// — Accessing various I/O streams
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: