From fd0026b5425bfc21ffa3903c1b1f276c5c533236 Mon Sep 17 00:00:00 2001 From: Giorgio Premi Date: Mon, 9 Nov 2015 13:05:16 +0100 Subject: [PATCH] Detect infinite script call recursion --- .../EventDispatcher/EventDispatcher.php | 33 +++++++++++++++++++ .../EventDispatcher/EventDispatcherTest.php | 32 ++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/src/Composer/EventDispatcher/EventDispatcher.php b/src/Composer/EventDispatcher/EventDispatcher.php index cfab0e8a9..46f22400c 100644 --- a/src/Composer/EventDispatcher/EventDispatcher.php +++ b/src/Composer/EventDispatcher/EventDispatcher.php @@ -45,6 +45,7 @@ class EventDispatcher protected $loader; protected $process; protected $listeners; + private $eventStack; /** * Constructor. @@ -58,6 +59,7 @@ class EventDispatcher $this->composer = $composer; $this->io = $io; $this->process = $process ?: new ProcessExecutor($io); + $this->eventStack = array(); } /** @@ -145,6 +147,8 @@ class EventDispatcher { $listeners = $this->getListeners($event); + $this->pushEvent($event); + $return = 0; foreach ($listeners as $callable) { if (!is_string($callable) && is_callable($callable)) { @@ -198,6 +202,8 @@ class EventDispatcher } } + $this->popEvent(); + return $return; } @@ -381,4 +387,31 @@ class EventDispatcher { return '@' === substr($callable, 0, 1); } + + /** + * Push an event to the stack of active event + * + * @param Event $event + * @throws \RuntimeException + * @return number + */ + protected function pushEvent(Event $event) + { + $eventName = $event->getName(); + if (in_array($eventName, $this->eventStack)) { + throw new \RuntimeException(sprintf("Recursive call to '%s' detected", $eventName)); + } + + return array_push($this->eventStack, $eventName); + } + + /** + * Pops the active event from the stack + * + * @return mixed + */ + protected function popEvent() + { + return array_pop($this->eventStack); + } } diff --git a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php index 0fde8f48b..afe4715d5 100644 --- a/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php +++ b/tests/Composer/Test/EventDispatcher/EventDispatcherTest.php @@ -201,6 +201,38 @@ class EventDispatcherTest extends TestCase $dispatcher->dispatch('root', new CommandEvent('root', $composer, $io)); } + /** + * @expectedException RuntimeException + */ + public function testDispatcherDetectInfiniteRecursion() + { + $process = $this->getMock('Composer\Util\ProcessExecutor'); + $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher') + ->setConstructorArgs(array( + $composer = $this->getMock('Composer\Composer'), + $io = $this->getMock('Composer\IO\IOInterface'), + $process, + )) + ->setMethods(array( + 'getListeners', + )) + ->getMock(); + + $dispatcher->expects($this->atLeastOnce()) + ->method('getListeners') + ->will($this->returnCallback(function (Event $event) { + if ($event->getName() === 'root') { + return array('@recurse'); + } elseif ($event->getName() === 'recurse') { + return array('@root'); + } + + return array(); + })); + + $dispatcher->dispatch('root', new CommandEvent('root', $composer, $io)); + } + private function getDispatcherStubForListenersTest($listeners, $io) { $dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')