diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index ee02381e5..d5bdb4c97 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -196,7 +196,9 @@ class EventDispatcher } try { - $return = $this->dispatch($scriptName, new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags)); + $scriptEvent = new Script\Event($scriptName, $event->getComposer(), $event->getIO(), $event->isDevMode(), $args, $flags); + $scriptEvent->setOriginatingEvent($event); + $return = $this->dispatch($scriptName, $scriptEvent); } catch (ScriptExecutionException $e) { $this->io->writeError(sprintf('Script %s was called via %s', $callable, $event->getName()), true, IOInterface::QUIET); throw $e; diff --git a/src/Composer/Script/Event.php b/src/Composer/Script/Event.php index 138f43c1a..5fab172bf 100644 --- a/src/Composer/Script/Event.php +++ b/src/Composer/Script/Event.php @@ -39,6 +39,11 @@ class Event extends BaseEvent */ private $devMode; + /** + * @var BaseEvent + */ + private $originatingEvent; + /** * Constructor. * @@ -55,6 +60,7 @@ class Event extends BaseEvent $this->composer = $composer; $this->io = $io; $this->devMode = $devMode; + $this->originatingEvent = null; } /** @@ -86,4 +92,42 @@ class Event extends BaseEvent { return $this->devMode; } + + /** + * Set the originating event. + * + * @return \Composer\EventDispatcher\Event|null + */ + public function getOriginatingEvent() + { + return $this->originatingEvent; + } + + /** + * Set the originating event. + * + * @param \Composer\EventDispatcher\Event $event + * @return $this + */ + public function setOriginatingEvent(BaseEvent $event) + { + $this->originatingEvent = $this->calculateOriginatingEvent($event); + + return $this; + } + + /** + * Returns the upper-most event in chain. + * + * @param \Composer\EventDispatcher\Event $event + * @return \Composer\EventDispatcher\Event + */ + private function calculateOriginatingEvent(BaseEvent $event) + { + if ($event instanceof Event && $event->getOriginatingEvent()) { + return $this->calculateOriginatingEvent($event->getOriginatingEvent()); + } + + return $event; + } } diff --git a/tests/Composer/Test/Script/EventTest.php b/tests/Composer/Test/Script/EventTest.php new file mode 100644 index 000000000..b7c8cd9ff --- /dev/null +++ b/tests/Composer/Test/Script/EventTest.php @@ -0,0 +1,80 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Script; + +use Composer\Composer; +use Composer\Config; +use Composer\Script\Event; +use Composer\Test\TestCase; + +class EventTest extends TestCase +{ + public function testEventSetsOriginatingEvent() + { + $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(); + $composer = $this->createComposerInstance(); + + $originatingEvent = new \Composer\EventDispatcher\Event('originatingEvent'); + + $scriptEvent = new Event('test', $composer, $io, true); + + $this->assertNull( + $scriptEvent->getOriginatingEvent(), + 'originatingEvent is initialized as null' + ); + + $scriptEvent->setOriginatingEvent($originatingEvent); + + $this->assertSame( + $originatingEvent, + $scriptEvent->getOriginatingEvent(), + 'getOriginatingEvent() SHOULD return test event' + ); + } + + public function testEventCalculatesNestedOriginatingEvent() + { + $io = $this->getMockBuilder('Composer\IO\IOInterface')->getMock(); + $composer = $this->createComposerInstance(); + + $originatingEvent = new \Composer\EventDispatcher\Event('upperOriginatingEvent'); + $intermediateEvent = new Event('intermediate', $composer, $io, true); + $intermediateEvent->setOriginatingEvent($originatingEvent); + + $scriptEvent = new Event('test', $composer, $io, true); + $scriptEvent->setOriginatingEvent($intermediateEvent); + + $this->assertNotSame( + $intermediateEvent, + $scriptEvent->getOriginatingEvent(), + 'getOriginatingEvent() SHOULD NOT return intermediate events' + ); + + $this->assertSame( + $originatingEvent, + $scriptEvent->getOriginatingEvent(), + 'getOriginatingEvent() SHOULD return upper-most event' + ); + } + + private function createComposerInstance() + { + $composer = new Composer; + $config = new Config; + $composer->setConfig($config); + $package = $this->getMockBuilder('Composer\Package\RootPackageInterface')->getMock(); + $composer->setPackage($package); + + return $composer; + } +}