Laravel Kernel实例化后的处理
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()
);
创建并获取Request对象
$request = Illuminate\Http\Request::capture()
\Illuminate\Http\Request extends \Symfony\Component\HttpFoundation\Request
public static function capture()
{
static::enableHttpMethodParameterOverride();
return static::createFromBase(SymfonyRequest::createFromGlobals());
}
public static function enableHttpMethodParameterOverride()
{
self::$httpMethodParameterOverride = true;
}
public static function createFromGlobals()
{
$server = $_SERVER;
// CLI mode
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'];
}
}
// 创建并返回\Symfony\Component\HttpFoundation\Request对象,实际上是用全局变量来实例化对应的类(可以对全局变量进行安全过滤),在赋予Request对象
$request = self::createRequestFromFactory($_GET, $_POST, array(), $_COOKIE, $_FILES, $server);
// 如果是以PUT|DELETE|PATCH方法进行的标准编码传输方式,就从原始数据的只读流解析数据到request属性(此属性其实对应的是POST键值对,PUT|DELETE|PATCH传输方式会被转成POST方式进行统一处理)
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) {
// 此方法必须返回Symfony\Component\HttpFoundation\Request的对象,否则抛异常
$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);
}
// 创建并返回\Illuminate\Http\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);
}
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 ($query !== null) {
$dup->query = new ParameterBag($query);
}
if ($request !== null) {
$dup->request = new ParameterBag($request);
}
if ($attributes !== null) {
$dup->attributes = new ParameterBag($attributes);
}
if ($cookies !== null) {
$dup->cookies = new ParameterBag($cookies);
}
if ($files !== null) {
$dup->files = new FileBag($files);
}
if ($server !== null) {
$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;
}
public function getContent($asResource = false)
{
$currentContentIsResource = is_resource($this->content);
if (PHP_VERSION_ID < 50600 && false === $this->content) {
throw new \LogicException('getContent() can only be called once when using the resource return type and PHP below 5.6.');
}
// 资源类型时的处理
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) {
$this->content = file_get_contents('php://input');
}
return $this->content;
}
总之:最后创建了一个解析了$_GET, $_POST, $_COOKIE, $_FILES, $_SERVER等变量之后的IlluminateHttpRequest类的对象
handle处理(核心)
public function handle($request)
{
try {
$request->enableHttpMethodParameterOverride();
$response = $this->sendRequestThroughRouter($request);
} catch (Exception $e) {
$this->reportException($e);
$response = $this->renderException($request, $e);
} catch (Throwable $e) {
$this->reportException($e = new FatalThrowableError($e));
$response = $this->renderException($request, $e);
}
event(new Events\RequestHandled($request, $response));
return $response;
}
// 核心方法
protected function sendRequestThroughRouter($request)
{
// 注入请求对象到服务容器,供后期使用
$this->app->instance('request', $request);
Facade::clearResolvedInstance('request');
// 启动应用(包括加载设置环境变量、加载配置文件、设置系统错误异常、Facade、启动各服务提供者的引导项等),后续分析
$this->bootstrap();
// 委托管道形式处理请求,这个是middleware实现的本质,后续分析
return (new Pipeline($this->app))
->send($request)
->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
->then($this->dispatchToRouter());
}
public static function clearResolvedInstance($name)
{
unset(static::$resolvedInstance[$name]);
}
public function bootstrap()
{
if (! $this->app->hasBeenBootstrapped()) {
$this->app->bootstrapWith($this->bootstrappers());
}
}
protected function bootstrappers()
{
#####################################################################
#$bootstrappers = [
# \Illuminate\Foundation\Bootstrap\LoadEnvironmentVariables::class,
# \Illuminate\Foundation\Bootstrap\LoadConfiguration::class,
# \Illuminate\Foundation\Bootstrap\HandleExceptions::class,
# \Illuminate\Foundation\Bootstrap\RegisterFacades::class,
# \Illuminate\Foundation\Bootstrap\RegisterProviders::class,
# \Illuminate\Foundation\Bootstrap\BootProviders::class,
#];
#####################################################################
return $this->bootstrappers;
}
public function bootstrapWith(array $bootstrappers)
{
$this->hasBeenBootstrapped = true;
foreach ($bootstrappers as $bootstrapper) {
// 启动前的事件触发
$this['events']->fire('bootstrapping: '.$bootstrapper, [$this]);
// 创建相应的对象并执行引导操作
$this->make($bootstrapper)->bootstrap($this);
// 启动后的事件触发
$this['events']->fire('bootstrapped: '.$bootstrapper, [$this]);
}
}
// 位于Illuminate\Events\Dispatcher文件,$payload用来传参给监听器,$halt表示是否终止后续事件的监听
public function fire($event, $payload = [], $halt = false)
{
return $this->dispatch($event, $payload, $halt);
}
public function dispatch($event, $payload = [], $halt = false)
{
list($event, $payload) = $this->parseEventAndPayload(
$event, $payload
);
// 若实现了广播类则加入广播队列
if ($this->shouldBroadcast($payload)) {
$this->broadcastEvent($payload[0]);
}
$responses = [];
// 获取此事件相关的监听事件函数
foreach ($this->getListeners($event) as $listener) {
$response = $listener($event, $payload); // 触发事件
if (! is_null($response) && $halt) {
return $response;
}
if ($response === false) {
break;
}
$responses[] = $response;
}
return $halt ? null : $responses;
}
public function getListeners($eventName)
{
$listeners = isset($this->listeners[$eventName]) ? $this->listeners[$eventName] : [];
$listeners = array_merge(
$listeners, $this->getWildcardListeners($eventName)
);
return class_exists($eventName, false)
? $this->addInterfaceListeners($eventName, $listeners) // 在$listeners增加接口监听事件
: $listeners;
}
$this['events']含义参考[kernel对象化]:
1. Illuminate\Foundation\Application extends Illuminate\Container\Container
2. Container implements ArrayAccess,故Application可以按数组形式读取。
3. public function offsetGet($key) { return $this->make($key); }
4. public function offsetSet($key, $value) { $this->bind($key, $value instanceof Closure ? $value : function () use ($value) { return $value; });}
5. public function __get($key) { return $this[$key]; }
6. public function __set($key, $value) { $this[$key] = $value; }
7. 所以$this['events'] 就是 $this->instances['events'] 对象($dispatcher);
8. 其他的$this['config']都是类似的。
9. $this['events']和$this->events一样,既可以数组形式访问,也可以按对象方式访问。
大体流程是: 创建获取请求对象(包括请求头和请求体)=>注入请求对象到服务容器=>配置系统运行环境=>发送请求