- Обзор плагинов в Composer
- Создание плагина Composer
- Обработчик событий в плагинах Composer
- Возможности плагина Composer
- Запуск плагинов Composer вручную
- Использование плагинов Composer
- Помощники плагинов Composer
- Дополнительные атрибуты плагинов Composer
- Автозагрузка плагина Composer
- Поддержка статического анализа плагинов в Composer
Обзор плагинов в Composer
При желании можно изменить или расширить функциональность Composer. Например, если ваша среда предъявляет особые требования к поведению Composer, которые не применимы к большинству пользователей, или если требуется реализовать что-то с помощью Composer таким образом, который нежелателен для большинства пользователей.
В этих случаях можно рассмотреть возможность создания плагина для работы с определенной специфической логикой.
Создание плагина Composer
Плагин — это обычный пакет Composer, который содержит собственный код как часть пакета, а также может зависеть от других пакетов.
Пакет плагина для Composer
Файл пакета является таким же, как и любой другой файл пакета, но со следующими требованиями:
- Атрибут
type
должен бытьcomposer-plugin
. - Атрибут extra должен содержать элемент
class
, определяющий имя класса подключаемого модуля (включая пространство имен). Если пакет содержит несколько плагинов, то это может быть массив имен классов. - Чтобы определить, с какими версиями 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
Заберите ссылку на статью к себе, чтобы потом легко её найти!
Раз уж досюда дочитали, то может может есть желание рассказать об этом месте своим друзьям, знакомым и просто мимо проходящим?
Не надо себя сдерживать! ;)