3.6. 创建 Composer 插件
概要
您可能希望使用自己的功能更改或扩展 Composer 的功能。例如,如果您的环境对 Composer 的行为提出了特殊要求,这些要求不适用于大多数用户,或者您希望以大多数用户不希望的方式使用 Composer 完成某些任务。
在这些情况下,您可以考虑创建一个插件来处理您的特定逻辑。
创建一个插件
插件是一个常规的 Composer 包,它将代码作为包的一部分提供,也可能依赖于其他包。
插件包
包文件与任何其他包文件相同,但具有以下要求:
- type 属性必须是
composer-plugin
. - extra 属性必须包含一个元素
class
,用于定义插件的类名(包括命名空间)。如果包中包含多个插件,则可以是类名称数组。 - 您需要依赖名为
composer-plugin-api
的特殊包来定义插件兼容的插件API版本。
所需的 composer-plugin-api
版本遵循与普通包相同的 规则 。
当前的 composer 插件 API 版本是 1.1.0。
常规的插件 composer.json
文件的示例(省略了自动加载部分):
{
"name": "my/plugin-package",
"type": "composer-plugin",
"require": {
"composer-plugin-api": "^1.1"
},
"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()
并让它返回一个数组。 数组键必须是 事件名称 并且对应的键值是要调用的此类中方法的名称。
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 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') {
$awsClient = new AwsClient($this->io, $this->composer->getConfig());
$s3RemoteFilesystem = new S3RemoteFilesystem($this->io, $event->getRemoteFilesystem()->getOptions(), $awsClient);
$event->setRemoteFilesystem($s3RemoteFilesystem);
}
}
}
插件功能
Composer 定义了一组可由插件实现的标准功能。通过为常见的插件需求提供明确的扩展点,减少了弄乱 Composer\Composer
内部状态的需求,使插件生态系统更加稳定。
Capable 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()
{
$this->setName('custom-plugin-command');
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln('Executing');
}
}
现在, custom-plugin-command
与 Composer 命令一起提供。
Composer 命令是基于 [Symfony Console Component](https://symfony.com/doc/current/components/console.html) 控制台组件的。
使用插件
插件包在安装后将会自动加载,如果在当前项目的已安装软件包列表中找到,则会在编译器启动时加载。此外,在加载本地项目插件之前,将使用 composer global 命令在 COMPOSER_HOME
目录中安装所有插件包。
您可以将
--no-plugins
选项传递给 Composer 命令以禁用所有已安装的插件。如果有插件造成错误,您希望更新或卸载它,这可能特别有用。