Laravel Kernel實例化後的處理

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類的對象php

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對象化]:bootstrap

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同樣,既能夠數組形式訪問,也能夠按對象方式訪問。

大致流程是: 建立獲取請求對象(包括請求頭和請求體)=>注入請求對象到服務容器=>配置系統運行環境=>發送請求segmentfault

相關文章
相關標籤/搜索