設定和使用外掛程式#

綜合概述#

您可能希望變更或擴充 Composer 的功能來符合您自身的需要。例如,如果您的環境對 Composer 的行為提出特殊的要求,而這些要求並不適用於大多數使用者,或者您希望透過大多數使用者不希望的方式,使用 Composer 來達成某件事。

在這些情況下,您可以考慮建立外掛程式來處理您的特定邏輯。

建立外掛程式#

外掛程式是一個一般性的 Composer 套件,其程式碼與套件一同發貨,且可能也依賴其他套件。

外掛程式套件#

套件檔案與其他任何套件檔案都相同,但有以下要求

  1. 類型(type)屬性必須為 composer-plugin
  2. 額外屬性(extra)必須包含定義外掛程式類別名稱(包含命名空間)的 class 元素。如果套件包含多個外掛程式,這可以是類別名稱的陣列。
  3. 您必須需要名為 composer-plugin-api 的特殊套件來定義您的外掛程式與哪些 Plugin API 版本相容。需要此套件實際上並不會包含任何額外的相依關係,它只會指定要使用哪個版本的 Plugin API。

附註:在開發外掛程式時,雖然沒有要求,但加入一個對 composer/composer 的 require-dev 相依關係,以在 IDE 自動完成 Composer 類別,會很有幫助。

所需的 composer-plugin-api 版本遵循普通套件規範的相同 規範

目前的 Composer 外掛程式 API 版本為 2.6.0

有效的外掛程式 composer.json 檔案範例(省略自動載入部分,以及 IDE 自動完成的 composer/composer 選擇性 require-dev 依賴項)

{
    "name": "my/plugin-package",
    "type": "composer-plugin",
    "require": {
        "composer-plugin-api": "^2.0"
    },
    "require-dev": {
        "composer/composer": "^2.0"
    },
    "extra": {
        "class": "My\\Plugin"
    }
}

外掛程式類別#

每個外掛程式都必須提供實作 Composer\Plugin\PluginInterface 的類別。外掛程式的 activate() 方法會在外掛程式載入後呼叫,並接收 Composer\Composer 的執行個體,以及 Composer\IO\IOInterface 的執行個體。透過使用這兩個物件,所有設定檔都可讀取,所有內部物件與狀態都可以依需求進行調整。

範例

<?php

namespace phpDocumentor\Composer;

use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;

class TemplateInstallerPlugin implements PluginInterface
{
    public function activate(Composer $composer, IOInterface $io)
    {
        $installer = new TemplateInstaller($io, $composer);
        $composer->getInstallationManager()->addInstaller($installer);
    }
}

事件處理常式#

此外,外掛程式可以實作 Composer\EventDispatcher\EventSubscriberInterface,以便在外掛程式載入時自動將其事件處理常式註冊至 EventDispatcher

若要將方法註冊至事件,請實作 getSubscribedEvents() 方法,並讓它傳回陣列。陣列鍵必須是 事件名稱,而值是要呼叫的此類別中方法的名稱。

請注意:如果您不知道要監聽哪個事件,您可以執行 Composer 指令,設定 COMPOSER_DEBUG_EVENTS=1 環境變數,這可能有助於您找出您正在尋找的事件。

public static function getSubscribedEvents()
{
    return array(
        'post-autoload-dump' => 'methodToBeCalled',
        // ^ event name ^         ^ method name ^
    );
}

預設情況下,事件處理常式的優先順序設定為 0。可以透過附加一個元組來變更優先順序,其中第一個值和以前一樣是方法名稱,而第二個值是一個代表優先順序的整數。較高的整數代表較高的優先順序。優先順序 2 會在優先順序 1 之前呼叫,依此類推。

public static function getSubscribedEvents()
{
    return array(
        // Will be called before events with priority 0
        'post-autoload-dump' => array('methodToBeCalled', 1)
    );
}

如果應該呼叫多個方法,則可以將元組陣列附加到每個事件。元組不需要包含優先順序。如果省略,預設會為 0。

public static function getSubscribedEvents()
{
    return array(
        'post-autoload-dump' => array(
            array('methodToBeCalled'      ), // Priority defaults to 0
            array('someOtherMethodName', 1), // This fires first
        )
    );
}

以下是完整的範例

<?php

namespace Naderman\Composer\AWS;

use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use Composer\Plugin\PluginEvents;
use Composer\Plugin\PreFileDownloadEvent;

class AwsPlugin implements PluginInterface, EventSubscriberInterface
{
    protected $composer;
    protected $io;

    public function activate(Composer $composer, IOInterface $io)
    {
        $this->composer = $composer;
        $this->io = $io;
    }

    public function deactivate(Composer $composer, IOInterface $io)
    {
    }

    public function uninstall(Composer $composer, IOInterface $io)
    {
    }

    public static function getSubscribedEvents()
    {
        return array(
            PluginEvents::PRE_FILE_DOWNLOAD => array(
                array('onPreFileDownload', 0)
            ),
        );
    }

    public function onPreFileDownload(PreFileDownloadEvent $event)
    {
        $protocol = parse_url($event->getProcessedUrl(), PHP_URL_SCHEME);

        if ($protocol === 's3') {
            // ...
        }
    }
}

外掛程式功能#

Composer 定義了一組外掛程式可以實作的標準功能。其目標是讓外掛程式生態系更穩定,因為它減少了對 Composer\Composer 內部狀態進行作業的需求,透過提供常見外掛程式需求的明確延伸點。

具備能力的 Plugins 類別必須實作 Composer\Plugin\Capable 介面,並在 getCapabilities() 方法中宣告其能力。此方法必須傳回一個陣列,其中 *key* 作為 Composer Capability 類別名稱,而 *value* 作為該能力的 Plugin 自身實作類別名稱

<?php

namespace My\Composer;

use Composer\Composer;
use Composer\IO\IOInterface;
use Composer\Plugin\PluginInterface;
use Composer\Plugin\Capable;

class Plugin implements PluginInterface, Capable
{
    public function activate(Composer $composer, IOInterface $io)
    {
    }

    public function getCapabilities()
    {
        return array(
            'Composer\Plugin\Capability\CommandProvider' => 'My\Composer\CommandProvider',
        );
    }
}

命令提供者#

Composer\Plugin\Capability\CommandProvider 能力允許為 Composer 註冊其他命令

<?php

namespace My\Composer;

use Composer\Plugin\Capability\CommandProvider as CommandProviderCapability;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Composer\Command\BaseCommand;

class CommandProvider implements CommandProviderCapability
{
    public function getCommands()
    {
        return array(new Command);
    }
}

class Command extends BaseCommand
{
    protected function configure(): void
    {
        $this->setName('custom-plugin-command');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $output->writeln('Executing');

        return 0;
    }
}

現在 custom-plugin-command 可與 Composer 命令並排使用。

Composer 命令基於 Symfony Console Component

手動執行 plugins#

可以使用 run-script 命令手動執行事件的 Plugins。這與 手動執行腳本 的方式相同。

如果是其他類型的 plugin,測試它的最佳方式可能是使用 路徑儲存庫 來要求測試專案中的 plugin。如果您在本地進行開發,並想要頻繁測試,您可以確保路徑儲存庫使用符號連結,因為變更會立即更新。否則,您必須在每次您想要安裝/重新執行它時執行 rm -rf vendor && composer update

使用 Plugins#

一安裝 Plugin 套件,就會自動載入,而如果它們是在目前的專案已安裝套件清單中找到的,則當 Composer 開始啟動時會載入。此外,使用 Composer 全域命令安裝在 COMPOSER_HOME 目錄中的所有 Plugin 套件都會在載入本機專案 Plugin 套件之前載入。

您可以將 --no-plugins 選項傳遞給 Composer 命令,以停用所有已安裝的 plugin。如果任何 plugin 造成錯誤,而您想要更新或解除安裝它時,這可能特別有用。

Plugin 幫手#

由於在 Composer 2 中,DownloaderInterface 有時可能會傳回 Promises,並且已分為比以前更多的步驟,因此我們提供一個 SyncHelper,以簡化下載和安裝套件。

Plugin Extra 屬性#

在 plugin 的 composer.json 中使用 extra 屬性,可以解鎖一些特殊的 plugin 能力。

class#

請參閱上方的內容,以取得 class 屬性的說明,以及它是如何運作的。

plugin-modifies-downloads#

有些特殊 plugin 需要在套件下載前更新套件下載網址。

自 Composer 2.0 起,所有套件在安裝前皆會先下載。這表示在第一次安裝時,當下載發生時外掛程式尚未安裝,且沒有機會即時更新網址。

在 composer.json 中指定 {"extra": {"plugin-modifies-downloads": true}} 會向 Composer 暗示應該先單獨安裝外掛程式,然後再繼續下載其他套件。不過,這會稍微拖慢整體安裝過程的速度,因此如果外掛程式並非必要,請勿使用此選項。

plugin-modifies-install-path#

有些特殊的外掛程式會修改套件的安裝路徑。

自 Composer 2.2.9 起,您可以在 composer.json 中指定 {"extra": {"plugin-modifies-install-path": true}},讓 Composer 知道應該儘早啟動外掛程式,以防止 Composer 假設套件安裝在實際上不同的位置而產生負面影響。

plugin-optional#

由於 Composer 外掛程式可用於執行對安裝運作中的應用程式而言有必要的動作,例如修改檔案儲存的路徑,無意間略過必要的的外掛程式可能會導致應用程式損毀。因此,在非互動模式下,如果新的外掛程式未列在 "allow-plugins" 中,Composer 便會失敗,以強迫使用者決定是否要執行外掛程式,避免無聲失敗。

自 Composer 2.5.3 起,您可以在外掛程式中使用設定 {"extra": {"plugin-optional": true}},讓 Composer 知道略過外掛程式不會造成災難性的後果,且如果外掛程式尚未列在 "allow-plugins" 中,它可以在非互動模式下安全地關閉。Composer 的下次互動執行仍會提示使用者選擇是否要啟用或關閉外掛程式。

外掛程式自動載入#

因為 Composer 在執行時期載入外掛程式,且為了確保依賴其他套件的外掛程式可以正確運作,會在每次載入外掛程式時建立一個執行時期自動載入程式。那個自動載入程式只組態為載入外掛程式的依賴項,因此您可能無法存取所有已安裝的套件。

靜態分析支援#

自 Composer 2.3.7 起,我們提供了一個 phpstan/rules.neon PHPStan 組態檔,這會在處理 Composer 外掛程式時提供額外的錯誤檢查。

PHPStan Extension Installer 搭配使用#

當您的外掛程式專案宣告對 phpstan/extension-installer 的依賴時,必要的組態檔會自動載入。

另類手動安裝#

要使用這個功能,您的 Composer 外掛程式專案需要一個 PHPStan 組態檔,其中包含 phpstan/rules.neon 檔案。

includes:
    - vendor/composer/composer/phpstan/rules.neon

// your remaining config..

發現錯字?此文件中有些錯誤?修改它!