Detect infinite script call recursion
parent
a25492d1b9
commit
fd0026b542
|
@ -45,6 +45,7 @@ class EventDispatcher
|
||||||
protected $loader;
|
protected $loader;
|
||||||
protected $process;
|
protected $process;
|
||||||
protected $listeners;
|
protected $listeners;
|
||||||
|
private $eventStack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
|
@ -58,6 +59,7 @@ class EventDispatcher
|
||||||
$this->composer = $composer;
|
$this->composer = $composer;
|
||||||
$this->io = $io;
|
$this->io = $io;
|
||||||
$this->process = $process ?: new ProcessExecutor($io);
|
$this->process = $process ?: new ProcessExecutor($io);
|
||||||
|
$this->eventStack = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -145,6 +147,8 @@ class EventDispatcher
|
||||||
{
|
{
|
||||||
$listeners = $this->getListeners($event);
|
$listeners = $this->getListeners($event);
|
||||||
|
|
||||||
|
$this->pushEvent($event);
|
||||||
|
|
||||||
$return = 0;
|
$return = 0;
|
||||||
foreach ($listeners as $callable) {
|
foreach ($listeners as $callable) {
|
||||||
if (!is_string($callable) && is_callable($callable)) {
|
if (!is_string($callable) && is_callable($callable)) {
|
||||||
|
@ -198,6 +202,8 @@ class EventDispatcher
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->popEvent();
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,4 +387,31 @@ class EventDispatcher
|
||||||
{
|
{
|
||||||
return '@' === substr($callable, 0, 1);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,6 +201,38 @@ class EventDispatcherTest extends TestCase
|
||||||
$dispatcher->dispatch('root', new CommandEvent('root', $composer, $io));
|
$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)
|
private function getDispatcherStubForListenersTest($listeners, $io)
|
||||||
{
|
{
|
||||||
$dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
|
$dispatcher = $this->getMockBuilder('Composer\EventDispatcher\EventDispatcher')
|
||||||
|
|
Loading…
Reference in New Issue