淺談 thinkphp composer 擴充套件包載入原理

2023-06-15 06:01:43

淺談 thinkphp composer 擴充套件包載入原理

本文將介紹 ThinkPHP 中 Composer 擴充套件包的載入原理,幫助讀者更好地理解和應用該功能。


前言


如題,今天感覺好久沒有更新部落格了。最近迷上了物聯網開發。一直在研究stm32、51這些東西。想起來前幾天群裡面有人問到tp擴充套件包原理。其實這個前幾年也就研究過。網上搜了搜發現相關文章也很少(也有可能是我搜尋姿勢不對)今天就來寫一篇thinkphp composer包載入原理


概覽


  1. 當進行 composer update 或者 composer require 操作時。則會執行service:discover這個命令。把當前所有已經載入的庫資訊都進行一次匹配。如果匹配到了think關鍵字的services屬性。則把服務類輸出成組態檔到vendor/services.php檔案中
  2. 當一次應用初始化(通常為一次存取開始時).則會引入vendor/services.php中的service服務類到當前應用內進行初始化

原始碼解析


composer包載入流程文字詳解 建議先閱讀一下這篇前兩年我寫的文章 Thinkphp6原始碼解析之分析 路由篇-請求流程

在第三步進入到Http->runWithRequest這個方法中後。可以看到又呼叫了initialize方法



追進這個方法可以看到



追進initialize方法看實現

    /**
     * 初始化應用
     * @access public
     * @return $this
     */
    public function initialize()
    {
        // 設定當前初始化狀態
        $this->initialized = true;
        
        // 設定應用開始時間
        $this->beginTime = microtime(true);
        
        // 獲取到php的記憶體
        $this->beginMem  = memory_get_usage();

        // 載入環境變數 例如當前應用目錄下的 .env檔案
        $this->loadEnv($this->envName);

        // 設定組態檔字尾
        $this->configExt = $this->env->get('config_ext', '.php');
        
        // 偵錯模式設定
        $this->debugModeInit();

        // 載入全域性初始化檔案
        $this->load();

        // 載入應用預設語言套件
        $this->loadLangPack();

        // 監聽AppInit
        $this->event->trigger(AppInit::class);
        
        // 設定php預設時區
        date_default_timezone_set($this->config->get('app.default_timezone', 'Asia/Shanghai'));

        // 初始化當前系統設定的預設服務
        foreach ($this->initializers as $initializer) {
			// 呼叫make函數生成物件。並且執行物件中的init方法
            $this->make($initializer)->init($this);
        }

        return $this;
    }

重點是初始化當前系統設定的預設服務這個$this->make($initializer)->init($this)函數,看看initializers屬性


/**
     * 應用初始化器
     * @var array
     */
    protected $initializers = [
        Error::class,
        RegisterService::class,
        BootService::class,
    ];


追到這裡就是關鍵了。上面把這裡面的類進行初始化。並且執行類中的init方法。直接看RegisterService::class類的init方法

public function init(App $app)
    {
        // 獲取當前專案根目錄。拼接上 vendor/services.php
        $file = $app->getRootPath() . 'vendor/services.php';

        $services = $this->services;

        if (is_file($file)) {
            $services = array_merge($services, include $file);
        }

        // 初始化services
        foreach ($services as $service) {
            if (class_exists($service)) {
                $app->register($service);
            }
        }
    }

讀到這裡的可以看看自己專案vendor目錄下是不是有一個services.php,接下來講一講composer.json這個檔案
在tp框架中的composer.json有這樣一個設定



這裡這個概念我直接讓chatgpt來解讀。解讀內容如下



接下來直接看service:discover這個命令。追到vendor\topthink\framework\src\think\console\command\ServiceDiscover.php檔案


  public function execute(Input $input, Output $output)
    {
        // 獲取到當前專案根目錄下的 vendor/composer/installed.json 檔案
        if (is_file($path = $this->app->getRootPath() . 'vendor/composer/installed.json')) {
            // json解析
            $packages = json_decode(@file_get_contents($path), true);
            // Compatibility with Composer 2.0
            if (isset($packages['packages'])) {
                $packages = $packages['packages'];
            }

            $services = [];
            foreach ($packages as $package) {
                // 判斷當前包是否在extra欄位裡面宣告了think關鍵字中的services屬性。如果宣告了就把services給裝載到services變數內
                if (!empty($package['extra']['think']['services'])) {
                    $services = array_merge($services, (array) $package['extra']['think']['services']);
                }
            }

            $header = '// This file is automatically generated at:' . date('Y-m-d H:i:s') . PHP_EOL . 'declare (strict_types = 1);' . PHP_EOL;

            // 用var_export函數把services變數列印成可讀性程式碼。並且寫入到根目錄vendor目錄下的services
            $content = '<?php ' . PHP_EOL . $header . "return " . var_export($services, true) . ';';

            file_put_contents($this->app->getRootPath() . 'vendor/services.php', $content);

            $output->writeln('<info>Succeed!</info>');
        }

一直到這就算結束了


寫在最後

如果覺得這篇文章對你有幫助。不妨點個贊留個關注再走