Ver código fonte

Add ability to call CLI-based commands from an event

John Kary 12 anos atrás
pai
commit
be90496952

+ 46 - 19
src/Composer/Script/EventDispatcher.php

@@ -16,6 +16,7 @@ use Composer\Autoload\AutoloadGenerator;
 use Composer\IO\IOInterface;
 use Composer\Composer;
 use Composer\DependencyResolver\Operation\OperationInterface;
+use Composer\Util\ProcessExecutor;
 
 /**
  * The Event Dispatcher.
@@ -34,6 +35,7 @@ class EventDispatcher
     protected $composer;
     protected $io;
     protected $loader;
+    protected $process;
 
     /**
      * Constructor.
@@ -41,10 +43,11 @@ class EventDispatcher
      * @param Composer    $composer The composer instance
      * @param IOInterface $io       The IOInterface instance
      */
-    public function __construct(Composer $composer, IOInterface $io)
+    public function __construct(Composer $composer, IOInterface $io, ProcessExecutor $process = null)
     {
         $this->composer = $composer;
         $this->io = $io;
+        $this->process = $process ?: new ProcessExecutor();
     }
 
     /**
@@ -78,24 +81,37 @@ class EventDispatcher
         $listeners = $this->getListeners($event);
 
         foreach ($listeners as $callable) {
-            $className = substr($callable, 0, strpos($callable, '::'));
-            $methodName = substr($callable, strpos($callable, '::') + 2);
-
-            if (!class_exists($className)) {
-                $this->io->write('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>');
-                continue;
-            }
-            if (!is_callable($callable)) {
-                $this->io->write('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>');
-                continue;
-            }
-
-            try {
-                $className::$methodName($event);
-            } catch (\Exception $e) {
-                $message = "Script %s handling the %s event terminated with an exception";
-                $this->io->write('<error>'.sprintf($message, $callable, $event->getName()).'</error>');
-                throw $e;
+            if ($this->isPhpScript($callable)) {
+                $className = substr($callable, 0, strpos($callable, '::'));
+                $methodName = substr($callable, strpos($callable, '::') + 2);
+
+                if (!class_exists($className)) {
+                    $this->io->write('<warning>Class '.$className.' is not autoloadable, can not call '.$event->getName().' script</warning>');
+                    continue;
+                }
+                if (!is_callable($callable)) {
+                    $this->io->write('<warning>Method '.$callable.' is not callable, can not call '.$event->getName().' script</warning>');
+                    continue;
+                }
+
+                try {
+                    $className::$methodName($event);
+                } catch (\Exception $e) {
+                    $message = "Script %s handling the %s event terminated with an exception";
+                    $this->io->write('<error>'.sprintf($message, $callable, $event->getName()).'</error>');
+                    throw $e;
+                }
+            } else {
+                $callback = function ($type, $buffer) use ($event, $callable) {
+                    $io = $event->getIO();
+                    if ('err' === $type) {
+                        $message = 'Script %s handling the %s event returned an error: %s';
+                        $io->write(sprintf('<error>'.$message.'</error>', $callable, $event->getName(), $buffer));
+                    } else {
+                        $io->write($buffer, false);
+                    }
+                };
+                $this->process->execute($callable, $callback);
             }
         }
     }
@@ -126,4 +142,15 @@ class EventDispatcher
 
         return $scripts[$event->getName()];
     }
+
+    /**
+     * Checks if string given references a class path and method
+     *
+     * @param string $callable
+     * @return boolean
+     */
+    protected function isPhpScript($callable)
+    {
+        return false !== strpos($callable, '::');
+    }
 }

+ 26 - 0
tests/Composer/Test/Script/EventDispatcherTest.php

@@ -35,6 +35,32 @@ class EventDispatcherTest extends TestCase
         $dispatcher->dispatchCommandEvent("post-install-cmd");
     }
 
+    public function testDispatcherCanExecuteCommandLineScripts()
+    {
+        $eventCliCommand = 'phpunit';
+
+        $process = $this->getMock('Composer\Util\ProcessExecutor');
+        $dispatcher = $this->getMockBuilder('Composer\Script\EventDispatcher')
+            ->setConstructorArgs(array(
+                $this->getMock('Composer\Composer'),
+                $this->getMock('Composer\IO\IOInterface'),
+                $process,
+            ))
+            ->setMethods(array('getListeners'))
+            ->getMock();
+
+        $listeners = array($eventCliCommand);
+        $dispatcher->expects($this->atLeastOnce())
+            ->method('getListeners')
+            ->will($this->returnValue($listeners));
+
+        $process->expects($this->once())
+            ->method('execute')
+            ->with($eventCliCommand);
+
+        $dispatcher->dispatchCommandEvent("post-install-cmd");
+    }
+
     private function getDispatcherStubForListenersTest($listeners, $io)
     {
         $dispatcher = $this->getMockBuilder('Composer\Script\EventDispatcher')