Document how to write and use plugins
parent
92b1ee2f7a
commit
98e5eabf75
|
@ -29,8 +29,8 @@ An example use-case would be:
|
||||||
|
|
||||||
> phpDocumentor features Templates that need to be installed outside of the
|
> phpDocumentor features Templates that need to be installed outside of the
|
||||||
> default /vendor folder structure. As such they have chosen to adopt the
|
> default /vendor folder structure. As such they have chosen to adopt the
|
||||||
> `phpdocumentor-template` [type][1] and create a Custom Installer to send
|
> `phpdocumentor-template` [type][1] and create a plugin providing the Custom
|
||||||
> these templates to the correct folder.
|
> Installer to send these templates to the correct folder.
|
||||||
|
|
||||||
An example composer.json of such a template package would be:
|
An example composer.json of such a template package would be:
|
||||||
|
|
||||||
|
@ -38,23 +38,24 @@ An example composer.json of such a template package would be:
|
||||||
"name": "phpdocumentor/template-responsive",
|
"name": "phpdocumentor/template-responsive",
|
||||||
"type": "phpdocumentor-template",
|
"type": "phpdocumentor-template",
|
||||||
"require": {
|
"require": {
|
||||||
"phpdocumentor/template-installer": "*"
|
"phpdocumentor/template-installer-plugin": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> **IMPORTANT**: to make sure that the template installer is present at the
|
> **IMPORTANT**: to make sure that the template installer is present at the
|
||||||
> time the template package is installed, template packages should require
|
> time the template package is installed, template packages should require
|
||||||
> the installer package.
|
> the plugin package.
|
||||||
|
|
||||||
## Creating an Installer
|
## Creating an Installer
|
||||||
|
|
||||||
A Custom Installer is defined as a class that implements the
|
A Custom Installer is defined as a class that implements the
|
||||||
[`Composer\Installer\InstallerInterface`][3] and is contained in a Composer
|
[`Composer\Installer\InstallerInterface`][3] and is usually distributed in a
|
||||||
package that has the [type][1] `composer-installer`.
|
Composer Plugin.
|
||||||
|
|
||||||
A basic Installer would thus compose of two files:
|
A basic Installer Plugin would thus compose of three files:
|
||||||
|
|
||||||
1. the package file: composer.json
|
1. the package file: composer.json
|
||||||
|
2. The Plugin class, e.g.: `My\Project\Composer\Plugin.php`, containing a class that implements `Composer\Plugin\PluginInterface`.
|
||||||
2. The Installer class, e.g.: `My\Project\Composer\Installer.php`, containing a class that implements `Composer\Installer\InstallerInterface`.
|
2. The Installer class, e.g.: `My\Project\Composer\Installer.php`, containing a class that implements `Composer\Installer\InstallerInterface`.
|
||||||
|
|
||||||
### composer.json
|
### composer.json
|
||||||
|
@ -62,35 +63,57 @@ A basic Installer would thus compose of two files:
|
||||||
The package file is the same as any other package file but with the following
|
The package file is the same as any other package file but with the following
|
||||||
requirements:
|
requirements:
|
||||||
|
|
||||||
1. the [type][1] attribute must be `composer-installer`.
|
1. the [type][1] attribute must be `composer-plugin`.
|
||||||
2. the [extra][2] attribute must contain an element `class` defining the
|
2. the [extra][2] attribute must contain an element `class` defining the
|
||||||
class name of the installer (including namespace). If a package contains
|
class name of the plugin (including namespace). If a package contains
|
||||||
multiple installers this can be array of class names.
|
multiple plugins this can be array of class names.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
{
|
{
|
||||||
"name": "phpdocumentor/template-installer",
|
"name": "phpdocumentor/template-installer-plugin",
|
||||||
"type": "composer-installer",
|
"type": "composer-installer-plugin",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": {"phpDocumentor\\Composer": "src/"}
|
"psr-0": {"phpDocumentor\\Composer": "src/"}
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"class": "phpDocumentor\\Composer\\TemplateInstaller"
|
"class": "phpDocumentor\\Composer\\TemplateInstallerPlugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
### The Plugin class
|
||||||
|
|
||||||
|
The class defining the Composer plugin must implement the
|
||||||
|
[`Composer\Plugin\PluginInterface`][3]. It can then register the Custom
|
||||||
|
Installer in its `activate()` method.
|
||||||
|
|
||||||
|
The class may be placed in any location and have any name, as long as it is
|
||||||
|
autoloadable and matches the `extra.class` element in the package definition.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
### The Custom Installer class
|
### The Custom Installer class
|
||||||
|
|
||||||
The class that executes the custom installation should implement the
|
The class that executes the custom installation should implement the
|
||||||
[`Composer\Installer\InstallerInterface`][3] (or extend another installer that
|
[`Composer\Installer\InstallerInterface`][4] (or extend another installer that
|
||||||
implements that interface).
|
implements that interface). It defines the [type][1] string as it will be
|
||||||
|
recognized by packages that will use this installer in the `supports()` method.
|
||||||
The class may be placed in any location and have any name, as long as it is
|
|
||||||
autoloadable and matches the `extra.class` element in the package definition.
|
|
||||||
It will also define the [type][1] string as it will be recognized by packages
|
|
||||||
that will use this installer in the `supports()` method.
|
|
||||||
|
|
||||||
> **NOTE**: _choose your [type][1] name carefully, it is recommended to follow
|
> **NOTE**: _choose your [type][1] name carefully, it is recommended to follow
|
||||||
> the format: `vendor-type`_. For example: `phpdocumentor-template`.
|
> the format: `vendor-type`_. For example: `phpdocumentor-template`.
|
||||||
|
@ -146,7 +169,7 @@ Example:
|
||||||
}
|
}
|
||||||
|
|
||||||
The example demonstrates that it is quite simple to extend the
|
The example demonstrates that it is quite simple to extend the
|
||||||
[`Composer\Installer\LibraryInstaller`][4] class to strip a prefix
|
[`Composer\Installer\LibraryInstaller`][5] class to strip a prefix
|
||||||
(`phpdocumentor/template-`) and use the remaining part to assemble a completely
|
(`phpdocumentor/template-`) and use the remaining part to assemble a completely
|
||||||
different installation path.
|
different installation path.
|
||||||
|
|
||||||
|
@ -155,5 +178,6 @@ different installation path.
|
||||||
|
|
||||||
[1]: ../04-schema.md#type
|
[1]: ../04-schema.md#type
|
||||||
[2]: ../04-schema.md#extra
|
[2]: ../04-schema.md#extra
|
||||||
[3]: https://github.com/composer/composer/blob/master/src/Composer/Installer/InstallerInterface.php
|
[3]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php
|
||||||
[4]: https://github.com/composer/composer/blob/master/src/Composer/Installer/LibraryInstaller.php
|
[4]: https://github.com/composer/composer/blob/master/src/Composer/Installer/InstallerInterface.php
|
||||||
|
[5]: https://github.com/composer/composer/blob/master/src/Composer/Installer/LibraryInstaller.php
|
||||||
|
|
|
@ -0,0 +1,147 @@
|
||||||
|
<!--
|
||||||
|
tagline: Modify and extend Composer's functionality
|
||||||
|
-->
|
||||||
|
|
||||||
|
# Setting up and using plugins
|
||||||
|
|
||||||
|
## Synopsis
|
||||||
|
|
||||||
|
You may wish to alter or expand Composer's functionality with your own. For
|
||||||
|
example if your environment poses special requirements on the behaviour of
|
||||||
|
Composer which do not apply to the majority of its users or if you wish to
|
||||||
|
accomplish something with composer in a way that is not desired by most users.
|
||||||
|
|
||||||
|
In these cases you could consider creating a plugin to handle your
|
||||||
|
specific logic.
|
||||||
|
|
||||||
|
## Creating a Plugin
|
||||||
|
|
||||||
|
A plugin is a regular composer package which ships its code as part of the
|
||||||
|
package and may also depend on further packages.
|
||||||
|
|
||||||
|
### Plugin Package
|
||||||
|
|
||||||
|
The package file is the same as any other package file but with the following
|
||||||
|
requirements:
|
||||||
|
|
||||||
|
1. the [type][1] attribute must be `composer-plugin`.
|
||||||
|
2. the [extra][2] attribute must contain an element `class` defining the
|
||||||
|
class name of the plugin (including namespace). If a package contains
|
||||||
|
multiple plugins this can be array of class names.
|
||||||
|
|
||||||
|
Additionally you must require the special package called `composer-plugin-api`
|
||||||
|
to define which composer API versions your plugin is compatible with. The
|
||||||
|
current composer plugin API version is 1.0.0.
|
||||||
|
|
||||||
|
For example
|
||||||
|
|
||||||
|
{
|
||||||
|
"name": "my/plugin-package",
|
||||||
|
"type": "composer-plugin",
|
||||||
|
"require": {
|
||||||
|
"composer-plugin-api": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
### Plugin Class
|
||||||
|
|
||||||
|
Every plugin has to supply a class which implements the
|
||||||
|
[`Composer\Plugin\PluginInterface`][3]. The `activate()` method of the plugin
|
||||||
|
is called after the plugin is loaded and receives an instance of
|
||||||
|
[`Composer\Composer`][4] as well as an instance of
|
||||||
|
[`Composer\IO\IOInterface`][5]. Using these two objects all configuration can
|
||||||
|
be read and all internal objects and state can be manipulated as desired.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
## Event Handler
|
||||||
|
|
||||||
|
Furthermore plugins may implement the
|
||||||
|
[`Composer\EventDispatcher\EventSubscriberInterface`][6] in order to have its
|
||||||
|
event handlers automatically registered with the `EventDispatcher` when the
|
||||||
|
plugin is loaded.
|
||||||
|
|
||||||
|
The events available for plugins are:
|
||||||
|
|
||||||
|
* **COMMAND**, is called at the beginning of all commands that load plugins.
|
||||||
|
It provides you with access to the input and output objects of the program.
|
||||||
|
* **PRE_FILE_DOWNLOAD**, is triggered before files are downloaded and allows
|
||||||
|
you to manipulate the `RemoteFilesystem` object prior to downloading files
|
||||||
|
based on the URL to be downloaded.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
## Using Plugins
|
||||||
|
|
||||||
|
Plugin packages are automatically loaded as soon as they are installed and will
|
||||||
|
be loaded when composer starts up if they are found in the current project's
|
||||||
|
list of installed packages. Additionally all plugin packages installed in the
|
||||||
|
`COMPOSER_HOME` directory using the composer global command are loaded before
|
||||||
|
local project plugins are loaded.
|
||||||
|
|
||||||
|
> You may pass the `--no-plugins` option to composer commands to disable all
|
||||||
|
> installed commands. This may be particularly helpful if any of the plugins
|
||||||
|
> causes errors and you wish to update or uninstall it.
|
||||||
|
|
||||||
|
[1]: ../04-schema.md#type
|
||||||
|
[2]: ../04-schema.md#extra
|
||||||
|
[3]: https://github.com/composer/composer/blob/master/src/Composer/Plugin/PluginInterface.php
|
||||||
|
[4]: https://github.com/composer/composer/blob/master/src/Composer/Composer.php
|
||||||
|
[5]: https://github.com/composer/composer/blob/master/src/Composer/IO/IOInterface.php
|
||||||
|
[6]: https://github.com/composer/composer/blob/master/src/Composer/EventDispatcher/EventSubscriberInterface.php
|
Loading…
Reference in New Issue