Skip to content

Commit

Permalink
move to reflection based
Browse files Browse the repository at this point in the history
  • Loading branch information
juliangut committed Sep 16, 2020
1 parent f9a09f6 commit 47d0447
Show file tree
Hide file tree
Showing 9 changed files with 324 additions and 133 deletions.
70 changes: 70 additions & 0 deletions README.md
Expand Up @@ -50,6 +50,76 @@ $eventBus = new EventBus($symfonyDispatcher);
$eventBus->dispatch($event);
```

#### Asynchronicity

To allow events to be handled asynchronously you should include `Gears\Event\Symfony\Dispatcher\AsyncEventHandler` in dispatcher's constructor

AsyncEventHandler requires an implementation of `Gears\Event\Async\EventQueue` which will be responsible for event queueing and an instance of `Gears\Event\Async\Discriminator\EventDiscriminator` used to discriminate which events should be queued

```php
use Gears\Event\Async\Discriminator\ParameterEventDiscriminator;
use Gears\Event\Async\Serializer\NativePhpEventSerializer;
use Gears\Event\Symfony\Dispatcher\AsyncEventQueueHandler;
use Gears\Event\Symfony\Dispatcher\ContainerAwareDispatcher;
use Gears\Event\Symfony\Dispatcher\EventBus;
use Gears\Event\Symfony\Dispatcher\Dispatcher;

/* @var \Gears\Event\Async\EventQueue $eventQueue */
$eventQueue = new EventQueueImplementation(new NativePhpEventSerializer());

$asyncEventHandler = new AsyncEventQueueHandler($eventQueue, new ParameterEventDiscriminator('async'));

$eventToHandlerMap = [];

$symfonyDispatcher = new Dispatcher($eventToHandlerMap, [$asyncEventHandler]);
// OR
/** @var \Psr\Container\ContainerInterface $container */
$symfonyDispatcher = new ContainerAwareDispatcher($container, $eventToHandlerMap, [$asyncEventHandler]);

$eventBus = new EventBus($symfonyDispatcher);

/** @var \Gears\Event\Event $event */
$eventBus->dispatch($event);
```

If you'd like to send different events to different message queues you can just add more instances of AsyncEventQueueHandler

To know more about how to create and configure an EventQueue head to [phpgears/event-async](https://github.com/phpgears/event-async)

##### Dequeueing

This part is highly dependent on your message queue, though event serializers can be used to deserialize queue messages

This is just an example of the process

```php
use Gears\Event\Async\Serializer\NativePhpEventSerializer;
use Gears\Event\Symfony\Dispatcher\ContainerAwareDispatcher;
use Gears\Event\Symfony\Dispatcher\EventBus;
use Gears\Event\Symfony\Dispatcher\Dispatcher;

$eventToHandlerMap = [];

$symfonyDispatcher = new Dispatcher($eventToHandlerMap);
// OR
/** @var \Psr\Container\ContainerInterface $container */
$symfonyDispatcher = new ContainerAwareDispatcher($container, $eventToHandlerMap);

$eventBus = new EventBus($symfonyDispatcher);
$serializer = new NativePhpEventSerializer();

while (true) {
/* @var your_message_queue_manager $queue */
$message = $queue->getMessage(); // extract messages from queue

if ($message !== null) {
$event = $serializer->fromSerialized($message);

$eventBus->dispatch($event);
}
}
```

## Contributing

Found a bug or have a feature request? [Please open a new issue](https://github.com/phpgears/event-symfony-event-dispatcher/issues). Have a look at existing issues before.
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Expand Up @@ -24,7 +24,8 @@
"prefer-stable": true,
"require": {
"php": "^7.1",
"phpgears/event": "~0.3.3",
"phpgears/event": "dev-reflection-based",
"phpgears/event-async": "dev-reflection-based",
"symfony/event-dispatcher": "^4.3"
},
"require-dev": {
Expand Down
2 changes: 0 additions & 2 deletions phpstan.neon
Expand Up @@ -2,5 +2,3 @@ parameters:
level: max
paths:
- src
ignoreErrors:
- '/^Parameter #2 \$listener of method .+\\EventDispatcher::addListener\(\) expects callable\(\): mixed, .+ given.$/'
62 changes: 62 additions & 0 deletions src/AsyncEventQueueHandler.php
@@ -0,0 +1,62 @@
<?php

/*
* event-symfony-event-dispatcher (https://github.com/phpgears/event-symfony-event-dispatcher).
* Event bus with Symfony Event Dispatcher.
*
* @license MIT
* @link https://github.com/phpgears/event-symfony-event-dispatcher
* @author Julián Gutiérrez <juliangut@gmail.com>
*/

declare(strict_types=1);

namespace Gears\Event\Symfony\Dispatcher;

use Gears\Event\Async\Discriminator\EventDiscriminator;
use Gears\Event\Async\EventQueue;
use Gears\Event\Async\QueuedEvent;
use Gears\Event\Event;

final class AsyncEventQueueHandler
{
/**
* Event queue.
*
* @var EventQueue
*/
private $eventQueue;

/**
* Event discriminator.
*
* @var EventDiscriminator
*/
private $discriminator;

/**
* AsyncEventHandler constructor.
*
* @param EventQueue $eventQueue
* @param EventDiscriminator $discriminator
*/
public function __construct(EventQueue $eventQueue, EventDiscriminator $discriminator)
{
$this->eventQueue = $eventQueue;
$this->discriminator = $discriminator;
}

/**
* Handle event.
*
* @param Event $event
*/
public function handle(Event $event): void
{
if (!$event instanceof QueuedEvent && $this->discriminator->shouldEnqueue($event)) {
$this->eventQueue->send(new QueuedEvent($event));

return;
}
}
}
102 changes: 17 additions & 85 deletions src/ContainerAwareDispatcher.php
Expand Up @@ -13,13 +13,11 @@

namespace Gears\Event\Symfony\Dispatcher;

use Gears\Event\Event;
use Gears\Event\EventHandler;
use Psr\Container\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcher as SymfonyEventDispatcher;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Contracts\EventDispatcher\Event as SymfonyEvent;

class ContainerAwareDispatcher extends SymfonyEventDispatcher implements EventDispatcher
class ContainerAwareDispatcher extends Dispatcher
{
/**
* @var ContainerInterface
Expand All @@ -29,106 +27,40 @@ class ContainerAwareDispatcher extends SymfonyEventDispatcher implements EventDi
/**
* ContainerAwareEventDispatcher constructor.
*
* @param ContainerInterface $container
* @param array<string, mixed> $listenersMap
* @param ContainerInterface $container
* @param array<string, mixed> $listenersMap
* @param array<AsyncEventQueueHandler> $asyncEventHandlers
*/
public function __construct(ContainerInterface $container, array $listenersMap = [])
{
parent::__construct();
public function __construct(
ContainerInterface $container,
array $listenersMap = [],
array $asyncEventHandlers = []
) {
parent::__construct($listenersMap, $asyncEventHandlers);

$this->container = $container;

foreach ($listenersMap as $eventName => $listeners) {
if (!\is_array($listeners)) {
$listeners = [$listeners];
}

foreach ($listeners as $listener) {
$this->addListener($eventName, $listener);
}
}
}

/**
* Adds an event subscriber.
*
* @param EventSubscriberInterface $subscriber
*/
public function addSubscriber(EventSubscriberInterface $subscriber): void
{
foreach ($subscriber::getSubscribedEvents() as $eventName => $params) {
if (!\is_array($params)) {
$params = [$params];
}

foreach ($params as $listener) {
if (\is_string($listener)) {
$this->addListener($eventName, $listener);
} else {
$this->addListener($eventName, $listener[0], $listener[1] ?? 0);
}
}
}
}

/**
* Adds an event listener that listens on the specified events.
*
* @param string $eventName
* @param callable|string $listener
* @param int $priority
* {@inheritdoc}
*/
public function addListener($eventName, $listener, $priority = 0): void
protected function assertListenerType($listener): void
{
if (!\is_string($listener)) {
throw new \InvalidArgumentException(\sprintf(
'Event handler must be a container entry, "%s" given',
\is_object($listener) ? \get_class($listener) : \gettype($listener)
));
}

parent::addListener($eventName, $listener, $priority);
}

/**
* Dispatches an event to all registered listeners.
*
* @param mixed $eventEnvelope
*
* @return SymfonyEvent
* {@inheritdoc}
*/
public function dispatch($eventEnvelope): SymfonyEvent
protected function dispatchEvent(iterable $listeners, Event $event): void
{
if ($eventEnvelope === null) {
throw new \InvalidArgumentException('Dispatched event cannot be empty');
}

if (!$eventEnvelope instanceof EventEnvelope) {
throw new \InvalidArgumentException(\sprintf(
'Dispatched event must implement "%s", "%s" given',
EventEnvelope::class,
\get_class($eventEnvelope)
));
}

$eventListeners = $this->getListeners($eventEnvelope->getWrappedEvent()->getEventType());
$this->dispatchEvent($eventListeners, $eventEnvelope);

return $eventEnvelope;
}

/**
* Dispatch event to registered listeners.
*
* @param string[] $listeners
* @param EventEnvelope $event
*/
private function dispatchEvent(array $listeners, EventEnvelope $event): void
{
$dispatchEvent = $event->getWrappedEvent();

/** @var string $listener */
foreach ($listeners as $listener) {
/* @var EventHandler $handler */
$handler = $this->container->get($listener);

if (!$handler instanceof EventHandler) {
Expand All @@ -139,7 +71,7 @@ private function dispatchEvent(array $listeners, EventEnvelope $event): void
));
}

$handler->handle($dispatchEvent);
$handler->handle($event);
}
}
}

0 comments on commit 47d0447

Please sign in to comment.