En ocasiones, los paquetes instalados mediante Composer necesitan realizar algunas tareas durante su instalación, como por ejemplo instalar elementos fuera del directorio vendor/
por defecto.
Composer permite crear instaladores propios para definir toda esta lógica específica de cada paquete.
Ejecutando un instalador propio
Suponiendo que ya hayas definido un instalador propio tal y como se explica en la siguiente sección, utilizarlo es tan sencillo como definir el valor adecuado en la propiedad type
del archivo composer.json
del paquete.
Como cada instalador propio define el tipo de paquete que es capaz de instalar, en cuanto Composer encuentra un instalador capaz de instalar un tipo concreto de paquete, se descarta el instalador normal y se ejecuta el instalador propio.
Un ejemplo práctico de la utilidad de los instaladores propios es la librería phpDocumentor
, que contiene varias plantillas que deben instalarse fuera del tradicional directorio vendor/
. Para ello, sus creadores han decidido definir un tipo especial de paquete llamado phpdocumentor-template
y crear un instalador propio que instale las plantillas en el directorio correcto.
El siguiente ejemplo muestra el archivo composer.json
de un paquete de este tipo que contenga plantillas:
{
"name": "phpdocumentor/template-responsive",
"type": "phpdocumentor-template",
"require": {
"phpdocumentor/template-installer": "*"
}
}
Para asegurarte de que el instalador propio está disponible al instalar un paquete de ese tipo, es muy importante que el paquete añada a su instalador como dependencia mediante la propiedad require
.
Creando un instalador propio
Los instaladores propios son simplemente clases que implementan la interfaz Composer\Installer\InstallerInterface
y se encuentran dentro de algún paquete de Composer de tipo composer-installer
.
Por lo tanto, un instalador sencillo está compuesto por dos archivos:
- El archivo
composer.json
que define el paquete. - La clase del instalador, como por ejemplo
My\Project\Composer\Installer.php
(y que implementa la interfazComposer\Installer\InstallerInterface
).
El archivo composer.json
El archivo composer.json
es igual que el de cualquier otro paquete normal de Composer, salvo por las dos siguientes restricciones:
- La propiedad
type
debe sercomposer-installer
. - La propiedad
extra
debe contener una propiedad llamadaclass
y cuyo valor define el nombre completo de la clase del instalador (también es necesario incluir el namespace de la clase). Si el paquete define varios instaladores propios, este valor debe ser un array de nombres de clases.
Ejemplo:
{
"name": "phpdocumentor/template-installer",
"type": "composer-installer",
"license": "MIT",
"autoload": {
"psr-0": {"phpDocumentor\\Composer": "src/"}
},
"extra": {
"class": "phpDocumentor\\Composer\\TemplateInstaller"
}
}
La clase del instalador
La clase que define el instalador propio debe implementar la interfaz Composer\Installer\InstallerInterface
o extender de algún otro instalador que implemente esa interfaz.
Puedes utilizar cualquier nombre para esta clase y puedes colocarla en cualquier directorio, siempre que se pueda cargar de forma automática y su namespace sea el que se indica en la propiedad extra.class
del archivo composer.json
.
En esta clase también se define el tipo concreto de paquete (en este caso, tipo de instalador) mediante la comprobación que se realiza en el método supports()
.
Ten mucho cuidado al elegir el valor de la propiedad type
, ya que podría entrar en conflicto con los tipos definidos por otros paquetes ajenos a tí. Para evitar posibles colisiones, se recomienda que su valor siga el formato vendor-type
(primero el nombre de la persona o empresa que hace el paquete y después, el tipo de paquete concreto), como por ejemplo: phpdocumentor-template
.
La clase InstallerInterface
define los siguientes métodos (no olvides consultar su código fuente para ver en detalle los parámetros de cada método):
supports()
, permite comprobar si el valor de la propiedadtype
coincide con el tipo de paquete definido en el archivocomposer.json
(observa cómo se realiza esta comprobación en el ejemplo siguiente).isInstalled()
, indica si el paquete ya se encuentra instalado o no.install()
, este es el método más importante, ya que incluye toda la lógica propia que se debe ejecutar al instalar el paquete.update()
, este es el segundo método más importante, ya que incluye la lógica propia que se ejecuta cuando Composer se invoca con el comandoupdate
en vez deinstall
.uninstall()
, define la lógica que se ejecuta cuando se desinstala el paquete. Asegúrate de borrar y eliminar cualquier archivo o directorio propio de este paquete para dejar el proyecto tal y como estaba antes de instalar este paquete.getInstallPath()
, devuelve la ruta donde se va a instalar el paquete. Esta ruta es relativa respecto del directorio donde se encuentr el archivocomposer.json
.
Ejemplo:
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;
}
}
Este ejemplo muestra lo sencillo que es extender el instalador LibraryInstaller
para ajustarse a las necesidades particulares de una librería. En este caso, la librería elimina el prefijo phpdocumentor/template-
para instalar el paquete en un directorio completamente distinto al original.
El resultado es que en vez de instalarse en el directorio por defecto /vendor
, cualquier paquete de este tipo se instalará en el directorio /data/templates/<nombre_corto>
.