diff --git a/doc/faqs/triggers.md b/doc/faqs/triggers.md index 65fc3c846..237079e6e 100644 --- a/doc/faqs/triggers.md +++ b/doc/faqs/triggers.md @@ -3,13 +3,12 @@ ## What is a trigger? 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, -update). +project. This event is raised before and after each action (install, update). ## 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? @@ -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 `composer.json` or package's `composer.json`. -It is specified as an associative array of classes with her static method, -associated with the event's type. +It is specified as an array of classes with her static method, +in associative array define the event's type. 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: ```json { "extra": { "triggers": { - "MyVendor\MyPackage2\MyClass2::myStaticMethod2" : "post_install", - "MyVendor\MyPackage2\MyClass2::myStaticMethod3" : "post_update", + "post_install": [ + "MyVendor\\MyRootPackage\\MyClass::myStaticMethod" + ], + "post_update": [ + "MyVendor\\MyRootPackage\\MyClass::myStaticMethod2" + ] } }, "autoload": { "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: -The project's triggers are executed after the package's triggers. 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. diff --git a/src/Composer/Command/InstallCommand.php b/src/Composer/Command/InstallCommand.php index c10b0cfb7..f6f2594b9 100644 --- a/src/Composer/Command/InstallCommand.php +++ b/src/Composer/Command/InstallCommand.php @@ -13,9 +13,7 @@ namespace Composer\Command; use Composer\Trigger\TriggerEvents; - use Composer\Trigger\TriggerDispatcher; - use Composer\Autoload\AutoloadGenerator; use Composer\DependencyResolver; use Composer\DependencyResolver\Pool; @@ -69,7 +67,8 @@ EOT $dryRun = (Boolean) $input->getOption('dry-run'); $verbose = $dryRun || $input->getOption('verbose'); $composer = $this->getComposer(); - $dispatcher = new TriggerDispatcher($this->getApplication()); + $io = $this->getApplication()->getIO(); + $dispatcher = new TriggerDispatcher($this->getComposer(), $io); if ($preferSource) { $composer->getDownloadManager()->setPreferSource(true); diff --git a/src/Composer/IO/ConsoleIO.php b/src/Composer/IO/ConsoleIO.php index 3ef514564..0169d2908 100644 --- a/src/Composer/IO/ConsoleIO.php +++ b/src/Composer/IO/ConsoleIO.php @@ -46,38 +46,6 @@ class ConsoleIO implements IOInterface $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} */ diff --git a/src/Composer/IO/IOInterface.php b/src/Composer/IO/IOInterface.php index cb9c74e24..1b84daab7 100644 --- a/src/Composer/IO/IOInterface.php +++ b/src/Composer/IO/IOInterface.php @@ -18,39 +18,7 @@ namespace Composer\IO; * @author François Pluchino */ 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? * diff --git a/src/Composer/Trigger/GetTriggerEvent.php b/src/Composer/Trigger/GetTriggerEvent.php deleted file mode 100644 index 940e0d0e0..000000000 --- a/src/Composer/Trigger/GetTriggerEvent.php +++ /dev/null @@ -1,108 +0,0 @@ - - * Jordi Boggiano - * - * 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 - */ -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(); - } -} diff --git a/src/Composer/Trigger/TriggerDispatcher.php b/src/Composer/Trigger/TriggerDispatcher.php index ad80d7bd3..5bb9514a9 100644 --- a/src/Composer/Trigger/TriggerDispatcher.php +++ b/src/Composer/Trigger/TriggerDispatcher.php @@ -16,33 +16,36 @@ use Composer\Json\JsonFile; use Composer\Repository\FilesystemRepository; use Composer\Autoload\ClassLoader; use Composer\Package\PackageInterface; -use Composer\Console\Application; +use Composer\IO\IOInterface; use Composer\Composer; /** * The Trigger Dispatcher. * * 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 */ class TriggerDispatcher { - protected $application; + protected $composer; + protected $io; protected $loader; /** * 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(); } @@ -53,11 +56,11 @@ class TriggerDispatcher */ public function dispatch($eventName) { - $event = new GetTriggerEvent(); - - $event->setDispatcher($this); + $event = new TriggerEvent(); + $event->setName($eventName); - $event->setApplication($this->application); + $event->setComposer($this->composer); + $event->setIO($this->io); $this->doDispatch($event); } @@ -65,109 +68,72 @@ class TriggerDispatcher /** * 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); - foreach ($listeners as $method => $eventType) { - if ($eventType === $event->getName()) { - $className = substr($method, 0, strpos($method, '::')); - $methodName = substr($method, strpos($method, '::') + 2); + foreach ($listeners as $method) { + $className = substr($method, 0, strpos($method, '::')); + $methodName = substr($method, strpos($method, '::') + 2); - try { - $refMethod = new \ReflectionMethod($className, $methodName); + try { + $refMethod = new \ReflectionMethod($className, $methodName); - // execute only if all conditions are validates - if ($refMethod->isPublic() - && $refMethod->isStatic() - && !$refMethod->isAbstract() - && 1 === $refMethod->getNumberOfParameters()) { - $className::$methodName($event); - } + // execute only if all conditions are validates + if ($refMethod->isPublic() + && $refMethod->isStatic() + && !$refMethod->isAbstract() + && 1 === $refMethod->getNumberOfParameters()) { + $className::$methodName($event); + } - } catch (\ReflectionException $ex) {}//silent execpetion - } + } catch (\ReflectionException $ex) {}//silent execpetion } } /** * Register namespaces in ClassLoader. * - * @param GetTriggerEvent $event The event object + * @param TriggerEvent $event The event object * * @return array The listener classes with event type */ - protected function getListeners(GetTriggerEvent $event) + protected function getListeners(TriggerEvent $event) { - $listeners = array(); - $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(); - $al = $package->getAutoload(); - $searchListeners = array(); - $searchNamespaces = array(); - $listeners = array(); + $package = $this->composer->getPackage(); + $ex = $package->getExtra(); + $al = $package->getAutoload(); + $searchListeners = array(); + $searchNamespaces = array(); + $listeners = array(); $namespaces = array(); // get classes - if (isset($ex['triggers'])) { - foreach ($ex['triggers'] as $method => $event) { - $searchListeners[$method] = $event; + if (isset($ex['triggers'][$event->getName()])) { + foreach ($ex['triggers'][$event->getName()] as $method) { + $searchListeners[] = $method; } } // get namespaces if (isset($al['psr-0'])) { foreach ($al['psr-0'] as $ns => $path) { - $dir = $root ? realpath('.') : $installDir; - - $path = trim($dir . '/' . $path, '/'); + $path = trim(realpath('.') . '/' . $path, '/'); $searchNamespaces[$ns] = $path; } } - - // filter class::method have not a namespace registered - foreach ($searchNamespaces as $ns => $path) { - foreach ($searchListeners as $method => $event) { - if (0 === strpos($method, $ns)) { - $listeners[$method] = $event; - if (!in_array($ns, array_keys($namespaces))) { - $namespaces[$ns] = $path; - } + // filter class::method have not a namespace registered + foreach ($searchNamespaces as $ns => $path) { + foreach ($searchListeners as $method) { + if (0 === strpos($method, $ns)) { + $listeners[] = $method; + + if (!in_array($ns, array_keys($namespaces))) { + $namespaces[$ns] = $path; + } } } } diff --git a/src/Composer/Trigger/TriggerEvent.php b/src/Composer/Trigger/TriggerEvent.php new file mode 100644 index 000000000..1b1a968bb --- /dev/null +++ b/src/Composer/Trigger/TriggerEvent.php @@ -0,0 +1,99 @@ + + * Jordi Boggiano + * + * 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 + */ +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; + } +} diff --git a/src/Composer/Trigger/TriggerEvents.php b/src/Composer/Trigger/TriggerEvents.php index 19d542938..61392405f 100644 --- a/src/Composer/Trigger/TriggerEvents.php +++ b/src/Composer/Trigger/TriggerEvents.php @@ -51,7 +51,7 @@ class TriggerEvents * @var string */ const PRE_UPDATE = 'pre_update'; - + /** * The POST_UPDATE event occurs at end update packages. * @@ -62,4 +62,28 @@ class TriggerEvents * @var string */ 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 }