ThinkPHP6 原始碼分析之解析 Request

JaguarJack發表於2019-07-02

獲取 Request 對像

在進入正題之前,需要重點說一下 make 方法。因為從上個版本使用過來的人已經開始接受 Container 這個概念了,這個新版本的增強了 Container 的功能,建立 Request 物件的精髓就在 make 方法。倒不如說整個框架核心類都在使用這個方法。我們來看一下這個方法是如何建立物件的。具體說明請看每一段的註釋

public function make(string $abstract, array $vars = [], bool $newInstance = false)
{
    // 首先說明 instances 屬性是一個陣列,就是主要儲存容器物件
    // 如果物件存在與容器中並且不需要重新建立的話,就直接從容器中獲取
    if (isset($this->instances[$abstract]) && !$newInstance) {
        return $this->instances[$abstract];
    }
    // bind 屬性從上一個應用初始化已經接觸到了,
    // 就是容器物件的繫結標識,和 instances 陣列不同的是,
   // 它只是儲存了一個類字串而已,需要例項化後獲取物件例項,下面就是實現的該功能

    // 如果 bind 標識存在
    if (isset($this->bind[$abstract])) {
        // 從 bind 獲取
        $concrete = $this->bind[$abstract];
        // concrete 可能是一個類名或者是一個匿名函式
        // 匿名函式直接執行
        if ($concrete instanceof Closure) {
            $object = $this->invokeFunction($concrete, $vars);
        } else {
            // 否則繼續 make 建立,因為此時還沒建立出物件
            return $this->make($concrete, $vars, $newInstance);
        }
    } else {
        // 如果沒有在標識中 說明需要的類名真正的產出了,需要例項化。
        // 其實這裡才是真正的建立類,並且下面的方法直接依賴注入
        $object = $this->invokeClass($abstract, $vars);
    }
    // 對於不需要建立例項的類 直接放在容器中管理
    if (!$newInstance) {
        $this->instances[$abstract] = $object;
    }
    // 最後返回物件
    return $object;
}

可能到這裡看的還是有點糊塗,正好在這裡用 Request 建立來梳理一下。我們來看看建立 Request 的過程。

分析

下面這段程式碼可以的 run 方法中找到,這段程式碼需要仔細推敲,不然你無法知道這個 Request 物件到底是哪個?

//自動建立request物件
$request = $request ?? $this->app->make('request', [], true);
$this->app->instance('request', $request);

$newInstance 在 Request 建立時被設定為了 true,說明每次請求都需要重新建立。此時 instances 還未有 Request 物件,所以繼續忽略。
在進入 bind 之前,先看看 bind 裡面都有什麼了?

array(22) {
  ["app"]=>
  string(9) "think\App"
  ["cache"]=>
  string(11) "think\Cache"
  ["config"]=>
  string(12) "think\Config"
  ["console"]=>
  string(13) "think\Console"
  ["cookie"]=>
  string(12) "think\Cookie"
  ["db"]=>
  string(8) "think\Db"
  ["env"]=>
  string(9) "think\Env"
  ["event"]=>
  string(11) "think\Event"
  ["http"]=>
  string(10) "think\Http"
  ["lang"]=>
  string(10) "think\Lang"
  ["log"]=>
  string(9) "think\Log"
  ["middleware"]=>
  string(16) "think\Middleware"
  ["request"]=>
  string(13) "think\Request"
  ["response"]=>
  string(14) "think\Response"
  ["route"]=>
  string(11) "think\Route"
  ["session"]=>
  string(13) "think\Session"
  ["validate"]=>
  string(14) "think\Validate"
  ["view"]=>
  string(10) "think\View"
  ["filesystem"]=>
  string(16) "think\Filesystem"
  ["Psr\Log\LoggerInterface"]=>
  string(9) "think\Log"
  ["think\Request"]=>
  string(11) "app\Request"
  ["think\exception\Handle"]=>
  string(19) "app\ExceptionHandle"
}

預設已經有了很多類名,這些都是框架預設的,不需要管,我們只看 request 鍵名和 ‘think\Request‘,還有最後兩個 think\Requestthink\exception\Handle
是在框架初始化注入進來的,不清楚的可以看上一節。think\Request 就是在那個時候注入的。

下面進入正題,很明顯 'request' 在存在與 bind 中的,並且 request 的值並不是一個匿名函式,而是一個字串 think\Request,只能繼續 make 建立,找到 think\Request對應的值 app\Request,它並不存在與 bind 中,所以直接來解析這個類名。這裡大概就明白了,實際框架並沒有在使用 think\Request 物件,而是 app\Request 物件。在解析後,Request 物件也被放進了容器了。下面就是請求的執行過程了。

原文轉載於 ThinkPHP6 原始碼分析之解析 Request

相關文章