资讯专栏INFORMATION COLUMN

Laravel Kernel实例化后的处理

taohonghui / 450人阅读

摘要:所以就是对象其他的都是类似的。和一样,既可以数组形式访问,也可以按对象方式访问。大体流程是创建获取请求对象包括请求头和请求体注入请求对象到服务容器配置系统运行环境发送请求

Laravel Kernel实例化后的处理
$response = $kernel->handle(
    $request = IlluminateHttpRequest::capture()
);
创建并获取Request对象
$request = IlluminateHttpRequest::capture()

IlluminateHttpRequest extends SymfonyComponentHttpFoundationRequest

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"];
        }
    }
    // 创建并返回SymfonyComponentHttpFoundationRequest对象,实际上是用全局变量来实例化对应的类(可以对全局变量进行安全过滤),在赋予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) {
        // 此方法必须返回SymfonyComponentHttpFoundationRequest的对象,否则抛异常
        $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 SymfonyComponentHttpFoundationRequest.");
        }

        return $request;
    }

    return new static($query, $request, $attributes, $cookies, $files, $server, $content);
}
// 创建并返回IlluminateHttpRequest对象
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 EventsRequestHandled($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 = [
    #    IlluminateFoundationBootstrapLoadEnvironmentVariables::class,
    #    IlluminateFoundationBootstrapLoadConfiguration::class,
    #    IlluminateFoundationBootstrapHandleExceptions::class,      
    #    IlluminateFoundationBootstrapRegisterFacades::class,
    #    IlluminateFoundationBootstrapRegisterProviders::class,      
    #    IlluminateFoundationBootstrapBootProviders::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]);
    }
}
// 位于IlluminateEventsDispatcher文件,$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. IlluminateFoundationApplication extends IlluminateContainerContainer
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一样,既可以数组形式访问,也可以按对象方式访问。

大体流程是: 创建获取请求对象(包括请求头和请求体)=>注入请求对象到服务容器=>配置系统运行环境=>发送请求

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/22610.html

相关文章

  • Laravel 请求周期

    摘要:请求周期加载自动加载器获取应用对象实例化应用解析此对象贯穿全文主要过程设置基础路径基础绑定注册全局基础服务核心容器别名设置注册三个单例获取对象实例化此对象为应用的枢纽,将会协调各部分之间的工作,完成请求主要过程注入应用对象注入事件对象注入 Laravel 请求周期 加载 composer 自动加载器 require __DIR__./../bootstrap/autoload.php;...

    Cristalven 评论0 收藏0
  • Laravel学习:请求到响应的生命周期

    摘要:请求处理阶段请求处理阶段首先是准备请求处理的环境,包括环境加载服务提供者注册等环节,然后将请求实例通过中间件处理及通过路由和控制器的分发控制,使得不同的请求通过相应的处理程序进行处理并生成响应的过程。 Laravel请求到响应的整个执行过程,主要可以归纳为四个阶段,即程序启动准备阶段、请求实例化阶段、请求处理阶段、响应发送和程序终止阶段。 程序启动准备阶段 服务容器实例化 服务容器的实...

    OBKoro1 评论0 收藏0
  • Laravel 动态添加 Artisan 命令的最佳实践

    摘要:初步尝试既然最常见的注册命令的方式是修改类中的,那么一般正常人都会从这边开始下手。又要自己取出实例,又要自己调用方法,调用方法之前还有自己先把实例化这么繁琐,肯定不是运行时添加命令的最佳实践,所以我决定继续寻找更优解。 本文首发于我的博客,原文链接:https://blessing.studio/best-... 虽然 Laravel 官方文档提供的添加 Artisan Command...

    ninefive 评论0 收藏0
  • Laravel中间件原理

    摘要:直到所有中间件都执行完毕,最后在执行最后的即上述的方法如果上述有地方难懂的,可以参考这边文章内置函数在中的使用以上是在通过全局中间件时的大致流程,通过中间件和路由中间件也是一样的,都是采用管道流操作,详情可翻阅源码 简介 Laravel 中间件提供了一种方便的机制来过滤进入应用的 HTTP 请求, 如ValidatePostSize用来验证POST请求体大小、ThrottleReque...

    张宪坤 评论0 收藏0
  • Laravel学习笔记之bootstrap源码解析

    摘要:总结本文主要学习了启动时做的七步准备工作环境检测配置加载日志配置异常处理注册注册启动。 说明:Laravel在把Request通过管道Pipeline送入中间件Middleware和路由Router之前,还做了程序的启动Bootstrap工作,本文主要学习相关源码,看看Laravel启动程序做了哪些具体工作,并将个人的研究心得分享出来,希望对别人有所帮助。Laravel在入口index...

    xiaoxiaozi 评论0 收藏0

发表评论

0条评论

taohonghui

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<