1
0
Fork 0

Remove package trigger

Rename GetTriggerEvent on TriggerEvent
Remove get/set Application in TriggerEvent
Remove get/set Dispatcher in TriggerEvent
Add get/set Composer in TriggerEvent
Add get/set IO in TriggerEvent
Restaure ConsoleIO and IOInterface
Update Faq
pull/257/head
François Pluchino 2012-01-25 00:22:12 +01:00
parent b147210ff4
commit f017074655
8 changed files with 188 additions and 295 deletions

View File

@ -3,13 +3,12 @@
## What is a trigger? ## What is a trigger?
A trigger is an event that runs a script in a static method, defined by a A trigger is an event that runs a script in a static method, defined by a
package or project. This event is raised before and after each action (install, project. This event is raised before and after each action (install, update).
update).
## Where are the event types defined? ## Where are the event types defined?
It is in the constant property in `Composer\Trigger\TriggerEvents` class. It is in the constant property in `Composer\\Trigger\\TriggerEvents` class.
## How is it defined? ## How is it defined?
@ -17,41 +16,27 @@ It is in the constant property in `Composer\Trigger\TriggerEvents` class.
It is defined by adding the `triggers` key in the `extra` key to a project's It is defined by adding the `triggers` key in the `extra` key to a project's
`composer.json` or package's `composer.json`. `composer.json` or package's `composer.json`.
It is specified as an associative array of classes with her static method, It is specified as an array of classes with her static method,
associated with the event's type. in associative array define the event's type.
The PSR-0 must be defined, otherwise the trigger will not be triggered. The PSR-0 must be defined, otherwise the trigger will not be triggered.
For any given package:
```json
{
"extra": {
"triggers": {
"MyVendor\MyPackage\MyClass::myStaticMethod" : "post_install",
"MyVendor\MyPackage\MyClass::myStaticMethod2" : "post_update",
}
},
"autoload": {
"psr-0": {
"MyVendor\MyPackage": ""
}
}
}
```
For any given project: For any given project:
```json ```json
{ {
"extra": { "extra": {
"triggers": { "triggers": {
"MyVendor\MyPackage2\MyClass2::myStaticMethod2" : "post_install", "post_install": [
"MyVendor\MyPackage2\MyClass2::myStaticMethod3" : "post_update", "MyVendor\\MyRootPackage\\MyClass::myStaticMethod"
],
"post_update": [
"MyVendor\\MyRootPackage\\MyClass::myStaticMethod2"
]
} }
}, },
"autoload": { "autoload": {
"psr-0": { "psr-0": {
"MyVendor\MyPackage": "my/folder/path/that/contains/triggers/from/the/root/project" "MyVendor\\MyRootPackage": "my/folder/path/that/contains/triggers/from/the/root/project"
} }
} }
} }
@ -59,12 +44,4 @@ For any given project:
## Informations: ## Informations:
The project's triggers are executed after the package's triggers.
A declared trigger with non existent file will be ignored. A declared trigger with non existent file will be ignored.
For example:
If you declare a trigger for a package pre install, as this trigger isn't
downloaded yet, it won't run.
On the other hand, if you declare a pre-update package trigger, as the file
already exist, the actual vendor's version of the trigger will be run.

View File

@ -13,9 +13,7 @@
namespace Composer\Command; namespace Composer\Command;
use Composer\Trigger\TriggerEvents; use Composer\Trigger\TriggerEvents;
use Composer\Trigger\TriggerDispatcher; use Composer\Trigger\TriggerDispatcher;
use Composer\Autoload\AutoloadGenerator; use Composer\Autoload\AutoloadGenerator;
use Composer\DependencyResolver; use Composer\DependencyResolver;
use Composer\DependencyResolver\Pool; use Composer\DependencyResolver\Pool;
@ -69,7 +67,8 @@ EOT
$dryRun = (Boolean) $input->getOption('dry-run'); $dryRun = (Boolean) $input->getOption('dry-run');
$verbose = $dryRun || $input->getOption('verbose'); $verbose = $dryRun || $input->getOption('verbose');
$composer = $this->getComposer(); $composer = $this->getComposer();
$dispatcher = new TriggerDispatcher($this->getApplication()); $io = $this->getApplication()->getIO();
$dispatcher = new TriggerDispatcher($this->getComposer(), $io);
if ($preferSource) { if ($preferSource) {
$composer->getDownloadManager()->setPreferSource(true); $composer->getDownloadManager()->setPreferSource(true);

View File

@ -46,38 +46,6 @@ class ConsoleIO implements IOInterface
$this->helperSet = $helperSet; $this->helperSet = $helperSet;
} }
/**
* {@inheritDoc}
*/
public function getArguments()
{
return $this->input->getArguments();
}
/**
* {@inheritDoc}
*/
public function getArgument($name)
{
return $this->input->getArgument($name);
}
/**
* {@inheritDoc}
*/
public function getOptions()
{
return $this->input->getOptions();
}
/**
* {@inheritDoc}
*/
public function getOption($name)
{
return $this->input->getOption($name);
}
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */

View File

@ -19,38 +19,6 @@ namespace Composer\IO;
*/ */
interface IOInterface interface IOInterface
{ {
/**
* Returns all the given arguments merged with the default values.
*
* @return array
*/
function getArguments();
/**
* Gets argument by name.
*
* @param string $name The name of the argument
*
* @return mixed
*/
function getArgument($name);
/**
* Returns all the given options merged with the default values.
*
* @return array
*/
function getOptions();
/**
* Gets an option by name.
*
* @param string $name The name of the option
*
* @return mixed
*/
function getOption($name);
/** /**
* Is this input means interactive? * Is this input means interactive?
* *

View File

@ -1,108 +0,0 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Trigger;
use Composer\Console\Application;
/**
* The Trigger Event.
*
* @author François Pluchino <francois.pluchino@opendisplay.com>
*/
class GetTriggerEvent
{
/**
* @var TriggerDispatcher Dispatcher that dispatched this event
*/
private $dispatcher;
/**
* @var string This event's name
*/
private $name;
/**
* @var Application The application instance
*/
private $application;
/**
* Returns the TriggerDispatcher that dispatches this Event
*
* @return TriggerDispatcher
*/
public function getDispatcher()
{
return $this->dispatcher;
}
/**
* Stores the TriggerDispatcher that dispatches this Event
*
* @param TriggerDispatcher $dispatcher
*/
public function setDispatcher(TriggerDispatcher $dispatcher)
{
$this->dispatcher = $dispatcher;
}
/**
* Returns the event's name.
*
* @return string The event name
*/
public function getName()
{
return $this->name;
}
/**
* Stores the event's name.
*
* @param string $name The event name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Returns the application instance.
*
* @return Application
*/
public function getApplication()
{
return $this->application;
}
/**
* Stores the application instance.
*
* @param Application $application
*/
public function setApplication(Application $application)
{
$this->application = $application;
}
/**
* Returns the composer instance.
*
* @return Composer
*/
public function getComposer()
{
return $this->application->getComposer();
}
}

View File

@ -16,33 +16,36 @@ use Composer\Json\JsonFile;
use Composer\Repository\FilesystemRepository; use Composer\Repository\FilesystemRepository;
use Composer\Autoload\ClassLoader; use Composer\Autoload\ClassLoader;
use Composer\Package\PackageInterface; use Composer\Package\PackageInterface;
use Composer\Console\Application; use Composer\IO\IOInterface;
use Composer\Composer; use Composer\Composer;
/** /**
* The Trigger Dispatcher. * The Trigger Dispatcher.
* *
* Example in command: * Example in command:
* $dispatcher = new TriggerDispatcher($this->getApplication()); * $dispatcher = new TriggerDispatcher($this->getComposer(), $this->getApplication()->getIO());
* // ... * // ...
* $dispatcher->dispatch(TriggerEvents::PRE_INSTALL); * $dispatcher->dispatch(TriggerEvents::POST_INSTALL);
* // ... * // ...
* *
* @author François Pluchino <francois.pluchino@opendisplay.com> * @author François Pluchino <francois.pluchino@opendisplay.com>
*/ */
class TriggerDispatcher class TriggerDispatcher
{ {
protected $application; protected $composer;
protected $io;
protected $loader; protected $loader;
/** /**
* Constructor. * Constructor.
* *
* @param Application $application * @param Composer $composer The composer instance
* @param IOInterface $io The IOInterface instance
*/ */
public function __construct(Application $application) public function __construct(Composer $composer, IOInterface $io)
{ {
$this->application = $application; $this->composer = $composer;
$this->io = $io;
$this->loader = new ClassLoader(); $this->loader = new ClassLoader();
} }
@ -53,11 +56,11 @@ class TriggerDispatcher
*/ */
public function dispatch($eventName) public function dispatch($eventName)
{ {
$event = new GetTriggerEvent(); $event = new TriggerEvent();
$event->setDispatcher($this);
$event->setName($eventName); $event->setName($eventName);
$event->setApplication($this->application); $event->setComposer($this->composer);
$event->setIO($this->io);
$this->doDispatch($event); $this->doDispatch($event);
} }
@ -65,76 +68,41 @@ class TriggerDispatcher
/** /**
* Triggers the listeners of an event. * Triggers the listeners of an event.
* *
* @param GetTriggerEvent $event The event object to pass to the event handlers/listeners. * @param TriggerEvent $event The event object to pass to the event handlers/listeners.
*/ */
protected function doDispatch(GetTriggerEvent $event) protected function doDispatch(TriggerEvent $event)
{ {
$listeners = $this->getListeners($event); $listeners = $this->getListeners($event);
foreach ($listeners as $method => $eventType) { foreach ($listeners as $method) {
if ($eventType === $event->getName()) { $className = substr($method, 0, strpos($method, '::'));
$className = substr($method, 0, strpos($method, '::')); $methodName = substr($method, strpos($method, '::') + 2);
$methodName = substr($method, strpos($method, '::') + 2);
try { try {
$refMethod = new \ReflectionMethod($className, $methodName); $refMethod = new \ReflectionMethod($className, $methodName);
// execute only if all conditions are validates // execute only if all conditions are validates
if ($refMethod->isPublic() if ($refMethod->isPublic()
&& $refMethod->isStatic() && $refMethod->isStatic()
&& !$refMethod->isAbstract() && !$refMethod->isAbstract()
&& 1 === $refMethod->getNumberOfParameters()) { && 1 === $refMethod->getNumberOfParameters()) {
$className::$methodName($event); $className::$methodName($event);
} }
} catch (\ReflectionException $ex) {}//silent execpetion } catch (\ReflectionException $ex) {}//silent execpetion
}
} }
} }
/** /**
* Register namespaces in ClassLoader. * Register namespaces in ClassLoader.
* *
* @param GetTriggerEvent $event The event object * @param TriggerEvent $event The event object
* *
* @return array The listener classes with event type * @return array The listener classes with event type
*/ */
protected function getListeners(GetTriggerEvent $event) protected function getListeners(TriggerEvent $event)
{ {
$listeners = array(); $package = $this->composer->getPackage();
$composer = $this->application->getComposer();
$vendorDir = $composer->getInstallationManager()->getVendorPath(true);
$installedFile = $vendorDir . '/.composer/installed.json';
// get the list of package installed
// $composer->getRepositoryManager()->getLocalRepository() not used
// because the list is not refreshed for the post event
$fsr = new FilesystemRepository(new JsonFile($installedFile));
$packages = $fsr->getPackages();
foreach ($packages as $package) {
$listeners = array_merge_recursive($listeners, $this->getListenerClasses($package));
}
// add root package
$listeners = array_merge_recursive($listeners, $this->getListenerClasses($composer->getPackage(), true));
return $listeners;
}
/**
* Get listeners and register the namespace on Classloader.
*
* @param PackageInterface $package The package objet
* @param boolean $root For root composer
*
* @return array The listener classes with event type
*/
private function getListenerClasses(PackageInterface $package, $root = false)
{
$composer = $this->application->getComposer();
$installDir = $composer->getInstallationManager()->getVendorPath(true)
. '/' . $package->getName();
$ex = $package->getExtra(); $ex = $package->getExtra();
$al = $package->getAutoload(); $al = $package->getAutoload();
$searchListeners = array(); $searchListeners = array();
@ -143,27 +111,25 @@ class TriggerDispatcher
$namespaces = array(); $namespaces = array();
// get classes // get classes
if (isset($ex['triggers'])) { if (isset($ex['triggers'][$event->getName()])) {
foreach ($ex['triggers'] as $method => $event) { foreach ($ex['triggers'][$event->getName()] as $method) {
$searchListeners[$method] = $event; $searchListeners[] = $method;
} }
} }
// get namespaces // get namespaces
if (isset($al['psr-0'])) { if (isset($al['psr-0'])) {
foreach ($al['psr-0'] as $ns => $path) { foreach ($al['psr-0'] as $ns => $path) {
$dir = $root ? realpath('.') : $installDir; $path = trim(realpath('.') . '/' . $path, '/');
$path = trim($dir . '/' . $path, '/');
$searchNamespaces[$ns] = $path; $searchNamespaces[$ns] = $path;
} }
} }
// filter class::method have not a namespace registered // filter class::method have not a namespace registered
foreach ($searchNamespaces as $ns => $path) { foreach ($searchNamespaces as $ns => $path) {
foreach ($searchListeners as $method => $event) { foreach ($searchListeners as $method) {
if (0 === strpos($method, $ns)) { if (0 === strpos($method, $ns)) {
$listeners[$method] = $event; $listeners[] = $method;
if (!in_array($ns, array_keys($namespaces))) { if (!in_array($ns, array_keys($namespaces))) {
$namespaces[$ns] = $path; $namespaces[$ns] = $path;

View File

@ -0,0 +1,99 @@
<?php
/*
* This file is part of Composer.
*
* (c) Nils Adermann <naderman@naderman.de>
* Jordi Boggiano <j.boggiano@seld.be>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Composer\Trigger;
use Composer\Composer;
use Composer\IO\IOInterface;
/**
* The Trigger Event.
*
* @author François Pluchino <francois.pluchino@opendisplay.com>
*/
class TriggerEvent
{
/**
* @var string This event's name
*/
private $name;
/**
* @var Composer The composer instance
*/
private $composer;
/**
* @var IOInterface The IO instance
*/
private $io;
/**
* Returns the event's name.
*
* @return string The event name
*/
public function getName()
{
return $this->name;
}
/**
* Stores the event's name.
*
* @param string $name The event name
*/
public function setName($name)
{
$this->name = $name;
}
/**
* Returns the composer instance.
*
* @return Composer
*/
public function getComposer()
{
return $this->composer;
}
/**
* Stores the composer instance.
*
* @param Composer $composer
*/
public function setComposer(Composer $composer)
{
$this->composer = $composer;
}
/**
* Returns the IO instance.
*
* @return IOInterface
*/
public function getIO()
{
return $this->io;
}
/**
* Stores the IO instance.
*
* @param IOInterface $io
*/
public function setIO(IOInterface $io)
{
$this->io = $io;
}
}

View File

@ -62,4 +62,28 @@ class TriggerEvents
* @var string * @var string
*/ */
const POST_UPDATE = 'post_update'; const POST_UPDATE = 'post_update';
/**
* The PRE_UNINSTALL event occurs at begging uninstallation packages.
*
* This event allows you to execute a trigger after any other code in the
* composer is executed. The event listener method receives a
* Composer\Trigger\TriggerEvent instance.
*
* @var string
*/
const PRE_UNINSTALL = 'pre_uninstall';
//TODO add the dispatcher when the uninstall command will be doing
/**
* The PRE_UNINSTALL event occurs at end uninstallation packages.
*
* This event allows you to execute a trigger after any other code in the
* composer is executed. The event listener method receives a
* Composer\Trigger\TriggerEvent instance.
*
* @var string
*/
const POST_UNINSTALL = 'post_uninstall';
//TODO add the dispatcher when the uninstall command will be doing
} }