Плагины Composer. Использование и настройка.
Использование и настройка плагинов Composer
  1. Обзор плагинов в Composer
  2. Создание плагина Composer
    1. Пакет плагина для Composer
    2. Класс плагина Composer
  3. Обработчик событий в плагинах Composer
  4. Возможности плагина Composer
    1. Провайдер команд Composer
  5. Запуск плагинов Composer вручную
  6. Использование плагинов Composer
  7. Помощники плагинов Composer
  8. Дополнительные атрибуты плагинов Composer
    1. class
    2. plugin-modifies-downloads
    3. plugin-modifies-install-path
    4. plugin-optional
  9. Автозагрузка плагина Composer
  10. Поддержка статического анализа плагинов в Composer
    1. Использование в плагинах Composer PHPStan Extension Installer
    2. Альтернативная ручная установка плагина для Composer


Обзор плагинов в Composer

При желании можно изменить или расширить функциональность Composer. Например, если ваша среда предъявляет особые требования к поведению Composer, которые не применимы к большинству пользователей, или если требуется реализовать что-то с помощью Composer таким образом, который нежелателен для большинства пользователей.

В этих случаях можно рассмотреть возможность создания плагина для работы с определенной специфической логикой.

Создание плагина Composer

Плагин — это обычный пакет Composer, который содержит собственный код как часть пакета, а также может зависеть от других пакетов.

Пакет плагина для Composer

Файл пакета является таким же, как и любой другой файл пакета, но со следующими требованиями:

  1. Атрибут type должен быть composer-plugin.
  2. Атрибут extra должен содержать элемент class, определяющий имя класса подключаемого модуля (включая пространство имен). Если пакет содержит несколько плагинов, то это может быть массив имен классов.
  3. Чтобы определить, с какими версиями Plugin API совместим ваш плагин, необходимо подключить специальный пакет composer-plugin-api. Требование этого пакета фактически не включает никаких дополнительных зависимостей, а только определяет, какую версию API плагина следует использовать.

Примечание: При разработке плагина, хотя это и не обязательно, полезно добавить зависимость require-dev от composer/composer, чтобы обеспечить автодополнение классов Composer в IDE.

Требуемая версия composer-plugin-api подчиняется тем же правилам, что и обычные пакеты.

Текущая версия Composer plugin API составляет 2.3.0.

Пример корректного файла composer.json плагина (с опущенной частью автозагрузки и дополнительной зависимостью require-dev от composer/composer для автодополнения в IDE):

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

Класс плагина Composer

Каждый подключаемый модуль должен поставлять класс, реализующий интерфейс 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

Кроме того, плагины могут реализовать интерфейс 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(
        // Будет вызываться ПЕРЕД событиями с приоритетом 0
        'post-autoload-dump' => array('methodToBeCalled', 1)
    );
}

Если необходимо вызвать несколько методов, то к каждому событию может быть прикреплен массив кортежей. Кортежи не обязательно должны содержать приоритет. Если он опущен, то по умолчанию он будет равен 0.

public static function getSubscribedEvents()
{
    return array(
        'post-autoload-dump' => array(
            array('methodToBeCalled'      ), // Приоритет по умолчанию равен 0
            array('someOtherMethodName', 1), // Это запускается первым
        )
    );
}

Вот полный пример:

<?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\Composer, предоставляя явные точки расширения для общих требований плагинов.

Классы Capable Plugins должны реализовывать интерфейс Composer\Plugin\Capable и объявлять свои возможности в методе getCapabilities(). Этот метод должен возвращать массив, ключом которого является имя класса Composer Capability, а значением - имя класса реализации этой Capability в самом плагине:

<?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

Функциональность 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');
    }
}

Теперь команда custom-plugin-command доступна наряду с другими командами Composer.

Команды Composer основаны на компоненте Symfony Console Component.

Запуск плагинов Composer вручную.

Плагины для события могут быть запущены вручную с помощью команды run-script. Это работает так же, как и запуск скриптов вручную.

Если это плагин другого типа, то лучшим способом его проверки будет, вероятно, использование пути репозитория для требования плагина в тестовом проекте, а затем rm -rf vendor && composer update каждый раз, когда вы захотите установить/запустить его снова.

Использование плагинов Composer

Пакеты плагинов автоматически загружаются сразу после их установки и будут загружены при запуске Composer, если они есть в списке установленных пакетов текущего проекта. Кроме того, все пакеты плагинов, установленные в каталог COMPOSER_HOME с помощью глобальной команды Composer, загружаются до загрузки плагинов локального проекта.

Для отключения всех установленных плагинов в командах Composer можно передать опцию --no-plugins. Это может быть особенно полезно, если какой-либо из плагинов вызывает ошибки и его необходимо обновить или удалить.

Помощники плагинов Composer

Начиная с Composer 2, в связи с тем, что DownloaderInterface иногда может возвращать Promises и разбиваться на большее количество шагов, необходимо добавить SyncHelper, чтобы упростить загрузку и установку пакетов.

Дополнительные атрибуты плагинов Composer.

Некоторые специальные возможности плагина могут быть использованы с помощью дополнительных атрибутов в файле composer.json плагина.

class

О том, как работает атрибут class, см. выше.

plugin-modifies-downloads

Некоторые специальные плагины нуждаются в обновлении URL-адресов загрузки пакетов перед их загрузкой.

Начиная с версии Composer 2.0, все пакеты загружаются до их установки. Это означает, что при первой установке ваш плагин еще не установлен, когда происходит загрузка, и у него нет возможности вовремя обновить URL-адреса.

Указав {"extra": {"plugin-modifies-downloads": true}} в файле composer.json даст понять Composer'у, что плагин должен быть установлен самостоятельно, прежде чем приступать к загрузке остальных пакетов. Однако это несколько замедляет общий процесс установки, поэтому не следует использовать этот параметр в плагинах, которые не требуют этого в обязательном порядке.

plugin-modifies-install-path

Некоторые специальные плагины изменяют путь установки пакетов.

Начиная с версии Composer 2.2.9, появилась возможность указать {"extra": {"plugin-modifies-install-path": true}} в файле composer.json, чтобы сообщить Composer, что плагин должен быть активирован как можно скорее, чтобы избежать неприятных побочных эффектов, связанных с тем, что Composer считает, что пакеты установлены не в том месте, где они находятся на самом деле.

plugin-optional

Поскольку подключаемые модули Composer могут использоваться для выполнения действий, необходимых для установки работающего приложения, например, для изменения пути хранения файлов, случайный пропуск необходимых подключаемых модулей может привести к сбоям в работе приложения. Поэтому в режиме non-interactive Composer будет завершать работу, если новый плагин не указан в списке "allow-plugins", чтобы заставить пользователя принять решение о необходимости выполнения плагина и избежать тихих сбоев.

Начиная с версии Composer 2.5.3, можно использовать настройку {"extra": {"plugin-optional": true}} для своего плагина, чтобы сообщить Composer'у, что пропуск плагина не приведет к катастрофическим последствиям, и его можно смело отключать в неинтерактивном режиме, если он еще не внесен в список "allow-plugins". При следующем интерактивном запуске Composer все равно предложит пользователю выбрать, включить или отключить плагин.

Автозагрузка плагина Composer

Поскольку плагины загружаются Composer во время выполнения, для обеспечения корректной работы плагинов, зависящих от других пакетов, при загрузке плагина создается автозагрузчик времени выполнения. Этот автозагрузчик настроен на загрузку только зависимостей плагина, поэтому вы можете не иметь доступа ко всем установленным пакетам.

Поддержка статического анализа плагинов в Composer

Начиная с версии Composer 2.3.7 поставляется конфигурационный файл phpstan/rules.neon PHPStan, который обеспечивает дополнительную проверку ошибок при работе с плагинами Composer.

Использование в плагинах Composer PHPStan Extension Installer

Необходимые конфигурационные файлы будут автоматически загружены в том случае, если в проектах ваших плагинов объявлена зависимость от phpstan/extension-installer.

Альтернативная ручная установка плагина для Composer

Для его использования в проекте плагина Composer необходим конфигурационный файл PHPStan, включающий файл phpstan/rules.neon:

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

// оставшаяся конфигурация...

Перевод с английского официальной документации Composer:
https://getcomposer.org/doc/articles/plugins.md

Заберите ссылку на статью к себе, чтобы потом легко её найти!
Раз уж досюда дочитали, то может может есть желание рассказать об этом месте своим друзьям, знакомым и просто мимо проходящим?
Не надо себя сдерживать! ;)

Старт! Горячий старт на просторы интернета
Старт! Горячий старт на просторы интернета
Старт! Меню