diff --git a/libs/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php b/libs/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php index af0b6cef..76f0e387 100644 --- a/libs/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php +++ b/libs/Symfony/Component/EventDispatcher/ContainerAwareEventDispatcher.php @@ -121,7 +121,7 @@ class ContainerAwareEventDispatcher extends EventDispatcher public function getListeners($eventName = null) { if (null === $eventName) { - foreach (array_keys($this->listenerIds) as $serviceEventName) { + foreach ($this->listenerIds as $serviceEventName => $args) { $this->lazyLoad($serviceEventName); } } else { diff --git a/libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php b/libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php index b796a812..7653ccfb 100644 --- a/libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php +++ b/libs/Symfony/Component/EventDispatcher/Debug/TraceableEventDispatcher.php @@ -31,6 +31,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface private $called; private $dispatcher; + private $wrappedListeners; /** * Constructor. @@ -45,6 +46,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface $this->stopwatch = $stopwatch; $this->logger = $logger; $this->called = array(); + $this->wrappedListeners = array(); } /** @@ -68,6 +70,16 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface */ public function removeListener($eventName, $listener) { + if (isset($this->wrappedListeners[$eventName])) { + foreach ($this->wrappedListeners[$eventName] as $index => $wrappedListener) { + if ($wrappedListener->getWrappedListener() === $listener) { + $listener = $wrappedListener; + unset($this->wrappedListeners[$eventName][$index]); + break; + } + } + } + return $this->dispatcher->removeListener($eventName, $listener); } @@ -146,7 +158,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface $allListeners = $this->getListeners(); } catch (\Exception $e) { if (null !== $this->logger) { - $this->logger->info(sprintf('An exception was thrown while getting the uncalled listeners (%s)', $e->getMessage()), array('exception' => $e)); + $this->logger->info('An exception was thrown while getting the uncalled listeners.', array('exception' => $e)); } // unable to retrieve the uncalled listeners @@ -216,12 +228,15 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface $this->dispatcher->removeListener($eventName, $listener); $info = $this->getListenerInfo($listener, $eventName); $name = isset($info['class']) ? $info['class'] : $info['type']; - $this->dispatcher->addListener($eventName, new WrappedListener($listener, $name, $this->stopwatch)); + $wrappedListener = new WrappedListener($listener, $name, $this->stopwatch, $this); + $this->wrappedListeners[$eventName][] = $wrappedListener; + $this->dispatcher->addListener($eventName, $wrappedListener); } } private function postProcess($eventName) { + unset($this->wrappedListeners[$eventName]); $skipped = false; foreach ($this->dispatcher->getListeners($eventName) as $listener) { if (!$listener instanceof WrappedListener) { // #12845: a new listener was added during dispatch. @@ -259,7 +274,7 @@ class TraceableEventDispatcher implements TraceableEventDispatcherInterface } /** - * Returns information about the listener + * Returns information about the listener. * * @param object $listener The listener * @param string $eventName The event name diff --git a/libs/Symfony/Component/EventDispatcher/Debug/WrappedListener.php b/libs/Symfony/Component/EventDispatcher/Debug/WrappedListener.php index c501662b..e16627d6 100644 --- a/libs/Symfony/Component/EventDispatcher/Debug/WrappedListener.php +++ b/libs/Symfony/Component/EventDispatcher/Debug/WrappedListener.php @@ -25,12 +25,14 @@ class WrappedListener private $called; private $stoppedPropagation; private $stopwatch; + private $dispatcher; - public function __construct($listener, $name, Stopwatch $stopwatch) + public function __construct($listener, $name, Stopwatch $stopwatch, EventDispatcherInterface $dispatcher = null) { $this->listener = $listener; $this->name = $name; $this->stopwatch = $stopwatch; + $this->dispatcher = $dispatcher; $this->called = false; $this->stoppedPropagation = false; } @@ -56,7 +58,7 @@ class WrappedListener $e = $this->stopwatch->start($this->name, 'event_listener'); - call_user_func($this->listener, $event, $eventName, $dispatcher); + call_user_func($this->listener, $event, $eventName, $this->dispatcher ?: $dispatcher); if ($e->isStarted()) { $e->stop(); diff --git a/libs/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php b/libs/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php index afe3ecd1..7e74a37a 100644 --- a/libs/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php +++ b/libs/Symfony/Component/EventDispatcher/DependencyInjection/RegisterListenersPass.php @@ -91,8 +91,12 @@ class RegisterListenersPass implements CompilerPassInterface throw new \InvalidArgumentException(sprintf('The service "%s" must be public as event subscribers are lazy-loaded.', $id)); } + if ($def->isAbstract()) { + throw new \InvalidArgumentException(sprintf('The service "%s" must not be abstract as event subscribers are lazy-loaded.', $id)); + } + // We must assume that the class value has been correctly filled, even if the service is created by a factory - $class = $def->getClass(); + $class = $container->getParameterBag()->resolveValue($def->getClass()); $refClass = new \ReflectionClass($class); $interface = 'Symfony\Component\EventDispatcher\EventSubscriberInterface'; diff --git a/libs/Symfony/Component/EventDispatcher/Event.php b/libs/Symfony/Component/EventDispatcher/Event.php index dc39b05d..048bf0ac 100644 --- a/libs/Symfony/Component/EventDispatcher/Event.php +++ b/libs/Symfony/Component/EventDispatcher/Event.php @@ -77,7 +77,7 @@ class Event * * @param EventDispatcherInterface $dispatcher * - * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. * * @api */ @@ -91,12 +91,14 @@ class Event * * @return EventDispatcherInterface * - * @deprecated Deprecated in 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. + * @deprecated since version 2.4, to be removed in 3.0. The event dispatcher is passed to the listener call. * * @api */ public function getDispatcher() { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event dispatcher instance can be received in the listener call instead.', E_USER_DEPRECATED); + return $this->dispatcher; } @@ -105,12 +107,14 @@ class Event * * @return string * - * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call. * * @api */ public function getName() { + @trigger_error('The '.__METHOD__.' method is deprecated since version 2.4 and will be removed in 3.0. The event name can be received in the listener call instead.', E_USER_DEPRECATED); + return $this->name; } @@ -119,7 +123,7 @@ class Event * * @param string $name The event name. * - * @deprecated Deprecated in 2.4, to be removed in 3.0. The event name is passed to the listener call. + * @deprecated since version 2.4, to be removed in 3.0. The event name is passed to the listener call. * * @api */ diff --git a/libs/Symfony/Component/EventDispatcher/EventDispatcher.php b/libs/Symfony/Component/EventDispatcher/EventDispatcher.php index 3b032fb0..46c11100 100644 --- a/libs/Symfony/Component/EventDispatcher/EventDispatcher.php +++ b/libs/Symfony/Component/EventDispatcher/EventDispatcher.php @@ -68,7 +68,7 @@ class EventDispatcher implements EventDispatcherInterface return $this->sorted[$eventName]; } - foreach (array_keys($this->listeners) as $eventName) { + foreach ($this->listeners as $eventName => $eventListeners) { if (!isset($this->sorted[$eventName])) { $this->sortListeners($eventName); } diff --git a/libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php b/libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php index efb7c5be..9d9fc4d4 100644 --- a/libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php +++ b/libs/Symfony/Component/EventDispatcher/EventDispatcherInterface.php @@ -77,7 +77,7 @@ interface EventDispatcherInterface public function removeSubscriber(EventSubscriberInterface $subscriber); /** - * Gets the listeners of a specific event or all listeners. + * Gets the listeners of a specific event or all listeners sorted by descending priority. * * @param string $eventName The name of the event * diff --git a/libs/Symfony/Component/EventDispatcher/GenericEvent.php b/libs/Symfony/Component/EventDispatcher/GenericEvent.php index a8955ca4..6458180a 100644 --- a/libs/Symfony/Component/EventDispatcher/GenericEvent.php +++ b/libs/Symfony/Component/EventDispatcher/GenericEvent.php @@ -71,7 +71,7 @@ class GenericEvent extends Event implements \ArrayAccess, \IteratorAggregate return $this->arguments[$key]; } - throw new \InvalidArgumentException(sprintf('%s not found in %s', $key, $this->getName())); + throw new \InvalidArgumentException(sprintf('Argument "%s" not found.', $key)); } /** diff --git a/libs/Symfony/Component/EventDispatcher/README.md b/libs/Symfony/Component/EventDispatcher/README.md index 0fbc35e2..8031f4dd 100644 --- a/libs/Symfony/Component/EventDispatcher/README.md +++ b/libs/Symfony/Component/EventDispatcher/README.md @@ -23,5 +23,5 @@ Resources You can run the unit tests with the following command: $ cd path/to/Symfony/Component/EventDispatcher/ - $ composer.phar install + $ composer install $ phpunit diff --git a/libs/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php b/libs/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php index b9e41949..5ff5be84 100644 --- a/libs/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php +++ b/libs/Symfony/Component/EventDispatcher/Tests/AbstractEventDispatcherTest.php @@ -118,10 +118,21 @@ abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase $this->assertInstanceOf('Symfony\Component\EventDispatcher\Event', $this->dispatcher->dispatch(self::preFoo)); $event = new Event(); $return = $this->dispatcher->dispatch(self::preFoo, $event); - $this->assertEquals('pre.foo', $event->getName()); $this->assertSame($event, $return); } + /** + * @group legacy + */ + public function testLegacyDispatch() + { + $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); + + $event = new Event(); + $return = $this->dispatcher->dispatch(self::preFoo, $event); + $this->assertEquals('pre.foo', $event->getName()); + } + public function testDispatchForClosure() { $invoked = 0; @@ -239,8 +250,13 @@ abstract class AbstractEventDispatcherTest extends \PHPUnit_Framework_TestCase $this->assertFalse($this->dispatcher->hasListeners(self::preFoo)); } - public function testEventReceivesTheDispatcherInstance() + /** + * @group legacy + */ + public function testLegacyEventReceivesTheDispatcherInstance() { + $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); + $dispatcher = null; $this->dispatcher->addListener('test', function ($event) use (&$dispatcher) { $dispatcher = $event->getDispatcher(); diff --git a/libs/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php b/libs/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php index 47dd5da1..24e60024 100644 --- a/libs/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php +++ b/libs/Symfony/Component/EventDispatcher/Tests/Debug/TraceableEventDispatcherTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\EventDispatcher\Tests\Debug; use Symfony\Component\EventDispatcher\Debug\TraceableEventDispatcher; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\Event; @@ -86,6 +87,20 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase $this->assertEquals(array(), $tdispatcher->getNotCalledListeners()); } + public function testGetCalledListenersNested() + { + $tdispatcher = null; + $dispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $dispatcher->addListener('foo', function (Event $event, $eventName, $dispatcher) use (&$tdispatcher) { + $tdispatcher = $dispatcher; + $dispatcher->dispatch('bar'); + }); + $dispatcher->addListener('bar', function (Event $event) {}); + $dispatcher->dispatch('foo'); + $this->assertSame($dispatcher, $tdispatcher); + $this->assertCount(2, $dispatcher->getCalledListeners()); + } + public function testLogger() { $logger = $this->getMock('Psr\Log\LoggerInterface'); @@ -160,6 +175,19 @@ class TraceableEventDispatcherTest extends \PHPUnit_Framework_TestCase $dispatcher->dispatch('foo'); $this->assertTrue($nestedCall); } + + public function testListenerCanRemoveItselfWhenExecuted() + { + $eventDispatcher = new TraceableEventDispatcher(new EventDispatcher(), new Stopwatch()); + $listener1 = function ($event, $eventName, EventDispatcherInterface $dispatcher) use (&$listener1) { + $dispatcher->removeListener('foo', $listener1); + }; + $eventDispatcher->addListener('foo', $listener1); + $eventDispatcher->addListener('foo', function () {}); + $eventDispatcher->dispatch('foo'); + + $this->assertCount(1, $eventDispatcher->getListeners('foo'), 'expected listener1 to be removed'); + } } class EventSubscriber implements EventSubscriberInterface diff --git a/libs/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php b/libs/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php index b291e1ee..0fdd6372 100644 --- a/libs/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php +++ b/libs/Symfony/Component/EventDispatcher/Tests/DependencyInjection/RegisterListenersPassTest.php @@ -138,6 +138,58 @@ class RegisterListenersPassTest extends \PHPUnit_Framework_TestCase $registerListenersPass = new RegisterListenersPass(); $registerListenersPass->process($container); } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage The service "foo" must not be abstract as event subscribers are lazy-loaded. + */ + public function testAbstractEventSubscriber() + { + $container = new ContainerBuilder(); + $container->register('foo', 'stdClass')->setAbstract(true)->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } + + public function testEventSubscriberResolvableClassName() + { + $container = new ContainerBuilder(); + + $container->setParameter('subscriber.class', 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService'); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + + $definition = $container->getDefinition('event_dispatcher'); + $expected_calls = array( + array( + 'addSubscriberService', + array( + 'foo', + 'Symfony\Component\EventDispatcher\Tests\DependencyInjection\SubscriberService', + ), + ), + ); + $this->assertSame($expected_calls, $definition->getMethodCalls()); + } + + /** + * @expectedException \InvalidArgumentException + * @expectedExceptionMessage You have requested a non-existent parameter "subscriber.class" + */ + public function testEventSubscriberUnresolvableClassName() + { + $container = new ContainerBuilder(); + $container->register('foo', '%subscriber.class%')->addTag('kernel.event_subscriber', array()); + $container->register('event_dispatcher', 'stdClass'); + + $registerListenersPass = new RegisterListenersPass(); + $registerListenersPass->process($container); + } } class SubscriberService implements \Symfony\Component\EventDispatcher\EventSubscriberInterface diff --git a/libs/Symfony/Component/EventDispatcher/Tests/EventTest.php b/libs/Symfony/Component/EventDispatcher/Tests/EventTest.php index 8f2fb735..4bd26972 100644 --- a/libs/Symfony/Component/EventDispatcher/Tests/EventTest.php +++ b/libs/Symfony/Component/EventDispatcher/Tests/EventTest.php @@ -60,6 +60,9 @@ class EventTest extends \PHPUnit_Framework_TestCase $this->assertTrue($this->event->isPropagationStopped()); } + /** + * @group legacy + */ public function testLegacySetDispatcher() { $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); @@ -67,18 +70,27 @@ class EventTest extends \PHPUnit_Framework_TestCase $this->assertSame($this->dispatcher, $this->event->getDispatcher()); } + /** + * @group legacy + */ public function testLegacyGetDispatcher() { $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); $this->assertNull($this->event->getDispatcher()); } + /** + * @group legacy + */ public function testLegacyGetName() { $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); $this->assertNull($this->event->getName()); } + /** + * @group legacy + */ public function testLegacySetName() { $this->iniSet('error_reporting', -1 & ~E_USER_DEPRECATED); diff --git a/libs/Symfony/Component/EventDispatcher/composer.json b/libs/Symfony/Component/EventDispatcher/composer.json index 6c58d4e4..d7058629 100644 --- a/libs/Symfony/Component/EventDispatcher/composer.json +++ b/libs/Symfony/Component/EventDispatcher/composer.json @@ -3,7 +3,7 @@ "type": "library", "description": "Symfony EventDispatcher Component", "keywords": [], - "homepage": "http://symfony.com", + "homepage": "https://symfony.com", "license": "MIT", "authors": [ { @@ -12,13 +12,14 @@ }, { "name": "Symfony Community", - "homepage": "http://symfony.com/contributors" + "homepage": "https://symfony.com/contributors" } ], "require": { - "php": ">=5.3.3" + "php": ">=5.3.9" }, "require-dev": { + "symfony/phpunit-bridge": "~2.7", "symfony/dependency-injection": "~2.6", "symfony/expression-language": "~2.6", "symfony/config": "~2.0,>=2.0.5", @@ -30,13 +31,12 @@ "symfony/http-kernel": "" }, "autoload": { - "psr-0": { "Symfony\\Component\\EventDispatcher\\": "" } + "psr-4": { "Symfony\\Component\\EventDispatcher\\": "" } }, - "target-dir": "Symfony/Component/EventDispatcher", "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-master": "2.6-dev" + "dev-master": "2.7-dev" } } } diff --git a/libs/Symfony/Component/EventDispatcher/phpunit.xml.dist b/libs/Symfony/Component/EventDispatcher/phpunit.xml.dist index 9a7728e9..ae0586e0 100644 --- a/libs/Symfony/Component/EventDispatcher/phpunit.xml.dist +++ b/libs/Symfony/Component/EventDispatcher/phpunit.xml.dist @@ -7,9 +7,9 @@ bootstrap="vendor/autoload.php" > - - + + ./Tests/ diff --git a/libs/curl-easy/cURL/Event.php b/libs/curl-easy/cURL/Event.php index d98238c7..53804cf6 100644 --- a/libs/curl-easy/cURL/Event.php +++ b/libs/curl-easy/cURL/Event.php @@ -5,6 +5,18 @@ use Symfony\Component\EventDispatcher\Event as SymfonyEvent; class Event extends SymfonyEvent { - /** @var Response $response */ - public $response = null; + /** + * @var Response + */ + public $response; + + /** + * @var Request + */ + public $request; + + /** + * @var RequestsQueue + */ + public $queue; } diff --git a/libs/curl-easy/cURL/Request.php b/libs/curl-easy/cURL/Request.php index 461d8ad8..199ebfac 100644 --- a/libs/curl-easy/cURL/Request.php +++ b/libs/curl-easy/cURL/Request.php @@ -54,7 +54,7 @@ class Request extends EventDispatcher implements RequestInterface public function getOptions() { if (!isset($this->options)) { - $this->options = new Options; + $this->options = new Options(); } return $this->options; } @@ -97,6 +97,8 @@ class Request extends EventDispatcher implements RequestInterface * This function should be called after initializing a cURL * session and all the options for the session are set. * + * Warning: it doesn't fire 'complete' event. + * * @return Response */ public function send() @@ -113,32 +115,31 @@ class Request extends EventDispatcher implements RequestInterface } return $response; } - - protected function prepareQueue() - { - if (!isset($this->queue)) { - $request = $this; - $this->queue = new RequestsQueue; - $this->queue->addListener( - 'complete', - function ($event) use ($request) { - $request->dispatch('complete', $event); - } - ); - $this->queue->attach($this); - } - } - + + /** + * Creates new RequestsQueue with single Request attached to it + * and calls RequestsQueue::socketPerform() method. + * + * @see RequestsQueue::socketPerform() + */ public function socketPerform() { - $this->prepareQueue(); + if (!isset($this->queue)) { + $this->queue = new RequestsQueue(); + $this->queue->attach($this); + } return $this->queue->socketPerform(); } - + + /** + * Calls socketSelect() on previously created RequestsQueue + * + * @see RequestsQueue::socketSelect() + */ public function socketSelect($timeout = 1) { if (!isset($this->queue)) { - throw new Exception('Cannot select without perform before.'); + throw new Exception('You need to call socketPerform() before.'); } return $this->queue->socketSelect($timeout); } diff --git a/libs/curl-easy/cURL/RequestsQueue.php b/libs/curl-easy/cURL/RequestsQueue.php index 0758946e..d97661b2 100644 --- a/libs/curl-easy/cURL/RequestsQueue.php +++ b/libs/curl-easy/cURL/RequestsQueue.php @@ -2,9 +2,8 @@ namespace cURL; use Symfony\Component\EventDispatcher\EventDispatcher; -use Countable; -class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, Countable +class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, \Countable { /** * @var Options Default options for new Requests attached to RequestsQueue @@ -59,7 +58,7 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C public function getDefaultOptions() { if (!isset($this->defaultOptions)) { - $this->defaultOptions = new Options; + $this->defaultOptions = new Options(); } return $this->defaultOptions; } @@ -126,7 +125,7 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C unset($this->running[$request->getUID()]); $this->detach($request); - $event = new Event; + $event = new Event(); $event->request = $request; $event->response = new Response($request, curl_multi_getcontent($request->getHandle())); if ($result !== CURLE_OK) { @@ -134,6 +133,7 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C } $event->queue = $this; $this->dispatch('complete', $event); + $request->dispatch('complete', $event); } return $n; @@ -168,9 +168,11 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C */ protected function getRequestsNotRunning() { - return array_diff_key($this->queue, $this->running); + $map = $this->queue; + foreach($this->running as $k => $v) unset($map[$k]); + return $map; } - + /** * Download available data on socket. * @@ -200,12 +202,15 @@ class RequestsQueue extends EventDispatcher implements RequestsQueueInterface, C $mrc = curl_multi_exec($this->mh, $this->runningCount); } while ($mrc === CURLM_CALL_MULTI_PERFORM); $runningAfter = $this->runningCount; - - $completed = ($runningAfter < $runningBefore) ? $this->read() : 0; + + if ($runningAfter < $runningBefore) { + $this->read(); + } $notRunning = $this->getRequestsNotRunning(); } while (count($notRunning) > 0); - + // Why the loop? New requests might be added at runtime on 'complete' event. + // So we need to attach them to curl_multi handle immediately. return $this->count() > 0; }