EventDispatcher.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. <?php
  2. /*
  3. * This file is part of Composer.
  4. *
  5. * (c) Nils Adermann <naderman@naderman.de>
  6. * Jordi Boggiano <j.boggiano@seld.be>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Composer\Script;
  12. use Composer\Autoload\AutoloadGenerator;
  13. use Composer\IO\IOInterface;
  14. use Composer\Composer;
  15. use Composer\DependencyResolver\Operation\OperationInterface;
  16. use Composer\Util\ProcessExecutor;
  17. /**
  18. * The Event Dispatcher.
  19. *
  20. * Example in command:
  21. * $dispatcher = new EventDispatcher($this->getComposer(), $this->getApplication()->getIO());
  22. * // ...
  23. * $dispatcher->dispatch(ScriptEvents::POST_INSTALL_CMD);
  24. * // ...
  25. *
  26. * @author François Pluchino <francois.pluchino@opendisplay.com>
  27. * @author Jordi Boggiano <j.boggiano@seld.be>
  28. */
  29. class EventDispatcher
  30. {
  31. protected $composer;
  32. protected $io;
  33. protected $loader;
  34. protected $process;
  35. /**
  36. * Constructor.
  37. *
  38. * @param Composer $composer The composer instance
  39. * @param IOInterface $io The IOInterface instance
  40. * @param ProcessExecutor $process
  41. */
  42. public function __construct(Composer $composer, IOInterface $io, ProcessExecutor $process = null)
  43. {
  44. $this->composer = $composer;
  45. $this->io = $io;
  46. $this->process = $process ?: new ProcessExecutor();
  47. }
  48. /**
  49. * Dispatch a script event.
  50. *
  51. * @param string $eventName The constant in ScriptEvents
  52. * @param Event $event
  53. */
  54. public function dispatch($eventName, Event $event = null)
  55. {
  56. if (null == $event) {
  57. $event = new Event($eventName, $this->composer, $this->io);
  58. }
  59. $this->doDispatch($event);
  60. }
  61. /**
  62. * Dispatch a package event.
  63. *
  64. * @param string $eventName The constant in ScriptEvents
  65. * @param boolean $devMode Whether or not we are in dev mode
  66. * @param OperationInterface $operation The package being installed/updated/removed
  67. */
  68. public function dispatchPackageEvent($eventName, $devMode, OperationInterface $operation)
  69. {
  70. $this->doDispatch(new PackageEvent($eventName, $this->composer, $this->io, $devMode, $operation));
  71. }
  72. /**
  73. * Dispatch a command event.
  74. *
  75. * @param string $eventName The constant in ScriptEvents
  76. * @param boolean $devMode Whether or not we are in dev mode
  77. */
  78. public function dispatchCommandEvent($eventName, $devMode)
  79. {
  80. $this->doDispatch(new CommandEvent($eventName, $this->composer, $this->io, $devMode));
  81. }
  82. /**
  83. * Triggers the listeners of an event.
  84. *
  85. * @param Event $event The event object to pass to the event handlers/listeners.
  86. */
  87. protected function doDispatch(Event $event)
  88. {
  89. $listeners = $this->getListeners($event);
  90. foreach ($listeners as $callable) {
  91. if ($this->isPhpScript($callable)) {
  92. $className = substr($callable, 0, strpos($callable, '::'));
  93. $methodName = substr($callable, strpos($callable, '::') + 2);
  94. if (!class_exists($className)) {
  95. $this->io->write('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>');
  96. continue;
  97. }
  98. if (!is_callable($callable)) {
  99. $this->io->write('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>');
  100. continue;
  101. }
  102. try {
  103. $this->executeEventPhpScript($className, $methodName, $event);
  104. } catch (\Exception $e) {
  105. $message = "Script %s handling the %s event terminated with an exception";
  106. $this->io->write('<error>'.sprintf($message, $callable, $event->getName()).'</error>');
  107. throw $e;
  108. }
  109. } else {
  110. if (0 !== $this->process->execute($callable)) {
  111. $event->getIO()->write(sprintf('<error>Script %s handling the %s event returned with an error: %s</error>', $callable, $event->getName(), $this->process->getErrorOutput()));
  112. }
  113. }
  114. }
  115. }
  116. /**
  117. * @param string $className
  118. * @param string $methodName
  119. * @param Event $event Event invoking the PHP callable
  120. */
  121. protected function executeEventPhpScript($className, $methodName, Event $event)
  122. {
  123. $className::$methodName($event);
  124. }
  125. /**
  126. * @param Event $event Event object
  127. * @return array Listeners
  128. */
  129. protected function getListeners(Event $event)
  130. {
  131. $package = $this->composer->getPackage();
  132. $scripts = $package->getScripts();
  133. if (empty($scripts[$event->getName()])) {
  134. return array();
  135. }
  136. if ($this->loader) {
  137. $this->loader->unregister();
  138. }
  139. $generator = $this->composer->getAutoloadGenerator();
  140. $packages = array_merge(
  141. $this->composer->getRepositoryManager()->getLocalRepository()->getPackages(),
  142. $this->composer->getRepositoryManager()->getLocalDevRepository()->getPackages()
  143. );
  144. $packageMap = $generator->buildPackageMap($this->composer->getInstallationManager(), $package, $packages);
  145. $map = $generator->parseAutoloads($packageMap, $package);
  146. $this->loader = $generator->createLoader($map);
  147. $this->loader->register();
  148. return $scripts[$event->getName()];
  149. }
  150. /**
  151. * Checks if string given references a class path and method
  152. *
  153. * @param string $callable
  154. * @return boolean
  155. */
  156. protected function isPhpScript($callable)
  157. {
  158. return false === strpos($callable, ' ') && false !== strpos($callable, '::');
  159. }
  160. }