1
0
Fork 0
composer/doc/articles/custom-installers.md

5.7 KiB

Custom installers

Synopsis

At times it may be necessary for a package to require additional actions during installation, such as installing packages outside of the default vendor library.

In these cases you could consider creating a Custom Installer to handle your specific logic.

Calling a Custom Installer

Suppose that your project already has a Custom Installer for specific modules then invoking that installer is a matter of defining the correct type in your package file.

See the next chapter for an instruction how to create Custom Installers.

Every Custom Installer defines which type string it will recognize. Once recognized it will completely override the default installer and only apply its own logic.

An example use-case would be:

phpDocumentor features Templates that need to be installed outside of the default /vendor folder structure. As such they have chosen to adopt the phpdocumentor-template type and create a Custom Installer to send these templates to the correct folder.

An example composer.json of such a template would be:

{
    "name": "phpdocumentor/template-responsive",
    "type": "phpdocumentor-template"
}

With the package definition shown in the example will any project that includes this package try to find an appropriate installer and use that to execute this type.

IMPORTANT: the host project will not recognize the type if the installer's package is not included. Thus a composer.json consuming a template package will always need to require the template's installer as well.

Creating an Installer

A Custom Installer is a defined as a class that implements the \Composer\Installer\InstallerInterface and is contained in a Composer package that has the type composer-installer.

A basic Installer would thus compose of two files:

  1. the package file: composer.json
  2. The Installer class, i.e.: \Composer\Installer\MyInstaller.php

NOTE: The namespace does not need to be \Composer\Installer, it may be anything that you would want.

composer.json

The package file is the same as any other package file but with the following requirements:

  1. the type attribute must be composer-installer.
  2. the extra attribute must contain an element class defining the class name of the installer (including namespace).

Example:

{
  "name": "phpdocumentor/template-installer",
  "type": "composer-installer",
  "license": "MIT",
  "autoload": {
    "psr-0": {"phpDocumentor\\Composer": "src/"}
  },
  "extra": {"class": "\\phpDocumentor\\Composer\\TemplateInstaller"}
}

The Custom Installer class

The class that executes the custom installation should implement the \Composer\Installer\InstallerInterface (or extend another installer that implements that interface).

The class may be placed in any location and have any name, as long as it matches the extra.class element in the package definition. It will also define the type string as it will be recognized by packages that will use this installer in the supports() method.

NOTE: choose your type name carefully, it is recommended to follow the format: vendor-type. For example: phpdocumentor-template.

The InstallerInterface class defines the following methods (please see the source for the exact signature):

  • supports(), here you test whether the passed type matches the name that you declared for this installer (see the example).
  • isInstalled(), determines whether a supported package is installed or not.
  • install(), here you can determine the actions that need to be executed upon installation.
  • update(), here you define the behavior that is required when Composer is invoked with the update argument.
  • uninstall(), here you can determine the actions that need to be executed when the package needs to be removed.
  • getInstallPath(), this method should return the location where the package is to be installed, relative from the location of composer.json.

Example:

namespace phpDocumentor\Composer;

use Composer\Package\PackageInterface;
use Composer\Installer\LibraryInstaller;

class TemplateInstaller extends LibraryInstaller
{
    /**
     * {@inheritDoc}
     */
    public function getInstallPath(PackageInterface $package)
    {
        $prefix = substr($package->getPrettyName(), 0, 23);
        if ('phpdocumentor/template-' != $prefix) {
            throw new \InvalidArgumentException(
                'Unable to install template, phpdocumentor templates '
                .'should always start their package name with '
                .'"phpdocumentor/template-"'
            );
        }

        return 'data/templates/'.substr($package->getPrettyName(), 23);
    }

    /**
     * {@inheritDoc}
     */
    public function supports($packageType)
    {
        return ('phpdocumentor-template' === $packageType);
    }
}

The example demonstrates that it is quite simple to extend the \Composer\Installer\LibraryInstaller class to strip a prefix (phpdocumentor/template-) and use the remaining part to assemble a completely different installation path.

Instead of installing to /vendor will any package installed using this Installer be put in the /data/templates/<stripped name> folder.