diff --git a/src/Consumer.php b/src/Consumer.php index ebae3387..8efee91a 100644 --- a/src/Consumer.php +++ b/src/Consumer.php @@ -121,6 +121,9 @@ public function resume() * * @param Envelope $envelope * @param Queue $queue + * + * @throws \Exception + * @throws \Throwable */ public function invoke(Envelope $envelope, Queue $queue) { @@ -134,16 +137,10 @@ public function invoke(Envelope $envelope, Queue $queue) $queue->acknowledge($envelope); $this->dispatcher->dispatch(BernardEvents::ACKNOWLEDGE, new EnvelopeEvent($envelope, $queue)); - } catch (\Exception $e) { - // Make sure the exception is not interfering. - // Previously failing jobs handling have been moved to a middleware. - // - // Emit an event to let others log that exception - $this->dispatcher->dispatch(BernardEvents::REJECT, new RejectEnvelopeEvent($envelope, $queue, $e)); - - if ($this->options['stop-on-error']) { - throw $e; - } + } catch (\Throwable $error) { + $this->rejectDispatch($error, $envelope, $queue); + } catch (\Exception $exception) { + $this->rejectDispatch($exception, $envelope, $queue); } } @@ -178,4 +175,26 @@ protected function bind() pcntl_signal(SIGCONT, [$this, 'resume']); } } + + /** + * @param \Throwable|\Exception $exception note that the type-hint is missing due to PHP 5.x compat + * + * @param Envelope $envelope + * @param Queue $queue + * + * @throws \Exception + * @throws \Throwable + */ + private function rejectDispatch($exception, Envelope $envelope, Queue $queue) + { + // Make sure the exception is not interfering. + // Previously failing jobs handling have been moved to a middleware. + // + // Emit an event to let others log that exception + $this->dispatcher->dispatch(BernardEvents::REJECT, new RejectEnvelopeEvent($envelope, $queue, $exception)); + + if ($this->options['stop-on-error']) { + throw $exception; + } + } } diff --git a/src/Event/RejectEnvelopeEvent.php b/src/Event/RejectEnvelopeEvent.php index b1499525..949a83b0 100644 --- a/src/Event/RejectEnvelopeEvent.php +++ b/src/Event/RejectEnvelopeEvent.php @@ -13,11 +13,11 @@ class RejectEnvelopeEvent extends EnvelopeEvent protected $exception; /** - * @param Envelope $envelope - * @param Queue $queue - * @param \Exception $exception + * @param Envelope $envelope + * @param Queue $queue + * @param \Exception|\Throwable $exception */ - public function __construct(Envelope $envelope, Queue $queue, \Exception $exception) + public function __construct(Envelope $envelope, Queue $queue, $exception) { parent::__construct($envelope, $queue); @@ -25,7 +25,7 @@ public function __construct(Envelope $envelope, Queue $queue, \Exception $except } /** - * @return \Exception + * @return \Exception|\Throwable */ public function getException() { diff --git a/tests/ConsumerTest.php b/tests/ConsumerTest.php index 63a9da59..ea4d3fa7 100644 --- a/tests/ConsumerTest.php +++ b/tests/ConsumerTest.php @@ -13,6 +13,21 @@ class ConsumerTest extends \PHPUnit_Framework_TestCase { + /** + * @var SimpleRouter + */ + private $router; + + /** + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\PHPUnit_Framework_MockObject_MockObject + */ + private $dispatcher; + + /** + * @var Consumer + */ + private $consumer; + public function setUp() { $this->router = new SimpleRouter; @@ -180,4 +195,35 @@ public function testEnvelopeWillBeInvoked() $this->assertTrue($service->importUsers); } + + /** + * @requires PHP 7.0 + */ + public function testWillRejectDispatchOnThrowableError() + { + $this->router->add('ImportReport', new Fixtures\Service); + + $queue = new InMemoryQueue('send-newsletter'); + $queue->enqueue(new Envelope(new DefaultMessage('ImportReport'))); + + $this->dispatcher->expects(self::at(0))->method('dispatch')->with('bernard.ping'); + $this->dispatcher->expects(self::at(1))->method('dispatch')->with('bernard.invoke'); + + $this + ->dispatcher + ->expects(self::at(2)) + ->method('dispatch') + ->with( + 'bernard.reject', + self::callback(function (RejectEnvelopeEvent $rejectEnvelope) { + self::assertInstanceOf('TypeError', $rejectEnvelope->getException()); + + return true; + }) + ); + + self::setExpectedException('TypeError'); + + $this->consumer->tick($queue, ['stop-on-error' => true]); + } } diff --git a/tests/Driver/AbstractDoctrineDriverTest.php b/tests/Driver/AbstractDoctrineDriverTest.php index 1e7dd2f0..ec21b6b6 100644 --- a/tests/Driver/AbstractDoctrineDriverTest.php +++ b/tests/Driver/AbstractDoctrineDriverTest.php @@ -11,6 +11,16 @@ abstract class AbstractDoctrineDriverTest extends \PHPUnit_Framework_TestCase { + /** + * @var \Doctrine\DBAL\Connection + */ + private $connection; + + /** + * @var DoctrineDriver + */ + protected $driver; + public function setUp() { if (defined('HHVM_VERSION')) { @@ -149,4 +159,9 @@ protected function setUpDatabase() return $connection; } + + /** + * @return \Doctrine\DBAL\Connection + */ + protected abstract function createConnection(); } diff --git a/tests/Event/RejectEnvelopeEventTest.php b/tests/Event/RejectEnvelopeEventTest.php index e3f92230..7dbf8f6b 100644 --- a/tests/Event/RejectEnvelopeEventTest.php +++ b/tests/Event/RejectEnvelopeEventTest.php @@ -6,6 +6,16 @@ class RejectEnvelopeEventTest extends \PHPUnit_Framework_TestCase { + /** + * @var \Bernard\Envelope|\PHPUnit_Framework_MockObject_MockObject + */ + private $envelope; + + /** + * @var \Bernard\Queue|\PHPUnit_Framework_MockObject_MockObject + */ + private $queue; + public function setUp() { $this->envelope = $this->getMockBuilder('Bernard\Envelope') @@ -27,4 +37,15 @@ public function testRetrieveException() $this->assertSame($e, $event->getException()); } + + /** + * @requires PHP 7.0 + */ + public function testCanContainThrowable() + { + $error = new \TypeError(); + $event = new RejectEnvelopeEvent($this->envelope, $this->queue, $error); + + self::assertSame($error, $event->getException()); + } } diff --git a/tests/Fixtures/Service.php b/tests/Fixtures/Service.php index 4cecc99c..4a5c994d 100644 --- a/tests/Fixtures/Service.php +++ b/tests/Fixtures/Service.php @@ -20,4 +20,10 @@ public function createFile() { touch(__DIR__ . '/create_file.test'); } + + public function importReport(Report $report) + { + // note: the class hinted on this method does not exist on purpose, as calling this method should cause a + // Throwable to be thrown + } }