Skip to content

Commit

Permalink
Add RegexRouter
Browse files Browse the repository at this point in the history
  • Loading branch information
codeliner committed Oct 30, 2014
1 parent c93236d commit cf44253
Show file tree
Hide file tree
Showing 6 changed files with 356 additions and 5 deletions.
32 changes: 32 additions & 0 deletions docs/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,38 @@ $router->route('My.Event.OrderWasPayed')->to("delivery_processor");
$eventBus->utilize($router);
```

## Prooph\ServiceBus\Router\RegexRouter

The RegexRouter works with regular expressions to determine handlers for messages. It can be used together with a CommandBus and
an EventBus but behaves different for both. When routing a command the RegexRouter makes sure that only one pattern matches.
If more than one pattern matches it throws a `Prooph\ServiceBus\Exception\RuntimeException`. On the other hand when routing
an event each time a pattern matches the corresponding listener is added to the list of listeners.

```php
//You can provide the pattern list as an associative array in the constructor ...
$router = new RegexRouter(array('/^My\.Command\.Buy.*/' => new BuyArticleHandler()));

//... or using the programmatic api
$router->route('/^My\.Command\.Register.*/')->to(new RegisterUserHandler());

//Add the router to a CommandBus
$commandBus->utilize($router);

//When routing an event you can provide a list of listeners for each pattern ...
$router = new RegexRouter(array('/^My\.Event\.Article.*/' => array(new OrderCartUpdater(), new InventoryUpdater())));

//... or using the programmatic api
$router->route('/^My\.Event\.Article.*/')->to(new OrderCartUpdater());

//The RegexRouter does not provide a andTo method like the EventRouter.
//You need to call route again for the same pattern,
//otherwise the router throws an exception
$router->route('/^My\.Event\.Article.*/')->to(new InventoryUpdater());

//Add the router to an EventBus
$eventBus->utilize($router);
```

# Invoke Strategies

An invoke strategy knows how a message handler can be invoked. You can register many invoke strategies at once depending on
Expand Down
2 changes: 1 addition & 1 deletion src/Prooph/ServiceBus/Process/EventDispatch.php
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ public function setException(\Exception $exception)
}

/**
* @return null|Exception
* @return null|\Exception
*/
public function getException()
{
Expand Down
4 changes: 2 additions & 2 deletions src/Prooph/ServiceBus/Router/CommandRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function __construct(array $commandMap = null)
*/
public function attach(EventManagerInterface $events)
{
$this->listeners[] = $events->attach(CommandDispatch::ROUTE, array($this, "onRouteEvent"));
$this->listeners[] = $events->attach(CommandDispatch::ROUTE, array($this, "onRouteCommand"));
}

/**
Expand Down Expand Up @@ -111,7 +111,7 @@ public function to($commandHandler)
/**
* @param CommandDispatch $commandDispatch
*/
public function onRouteEvent(CommandDispatch $commandDispatch)
public function onRouteCommand(CommandDispatch $commandDispatch)
{
if (is_null($commandDispatch->getCommandName())) {
$commandDispatch->getLogger()->notice(
Expand Down
178 changes: 178 additions & 0 deletions src/Prooph/ServiceBus/Router/RegexRouter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php
/*
* This file is part of the prooph/service-bus.
* (c) Alexander Miertsch <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* Date: 30.10.14 - 22:29
*/

namespace Prooph\ServiceBus\Router;

use Prooph\ServiceBus\Exception\RuntimeException;
use Prooph\ServiceBus\Process\CommandDispatch;
use Prooph\ServiceBus\Process\EventDispatch;
use Zend\EventManager\AbstractListenerAggregate;
use Zend\EventManager\EventManagerInterface;

/**
* Class RegexRouter
*
* @package Prooph\ServiceBus\Router
* @author Alexander Miertsch <[email protected]>
*/
class RegexRouter extends AbstractListenerAggregate
{
const ALL = '/.*/';

/**
* @var array[array[pattern => handler], ...]
*/
protected $patternMap = array();

/**
* @var string
*/
protected $tmpPattern;

/**
* @param null|array[pattern => handler|handler[]] $patternMap
*/
public function __construct(array $patternMap = null)
{
if (! is_null($patternMap)) {
foreach ($patternMap as $pattern => $handler) {
if (is_array($handler)) {
foreach($handler as $singleHandler) $this->route($pattern)->to($singleHandler);
} else {
$this->route($pattern)->to($handler);
}

}
}
}

/**
* implementation will pass this to the aggregate.
*
* @param EventManagerInterface $events
*
* @return void
*/
public function attach(EventManagerInterface $events)
{
$identifiers = $events->getIdentifiers();

if (in_array('command_bus', $identifiers)) {
$this->listeners[] = $events->attach(CommandDispatch::ROUTE, array($this, 'onRouteCommand'), 100);
}

if (in_array('event_bus', $identifiers)) {
$this->listeners[] = $events->attach(EventDispatch::ROUTE, array($this, 'onRouteEvent'), 100);
}
}

/**
* @param string $pattern
* @return $this
* @throws \Prooph\ServiceBus\Exception\RuntimeException
*/
public function route($pattern)
{
\Assert\that($pattern)->notEmpty()->string();

if (! is_null($this->tmpPattern)) {
throw new RuntimeException(sprintf("pattern %s is not mapped to a handler.", $this->tmpPattern));
}

$this->tmpPattern = $pattern;

return $this;
}

/**
* @param string|object|callable $handler
* @return $this
* @throws \Prooph\ServiceBus\Exception\RuntimeException
* @throws \InvalidArgumentException
*/
public function to($handler)
{
if (! is_string($handler) && ! is_object($handler) && ! is_callable($handler)) {
throw new \InvalidArgumentException(sprintf(
"Invalid handler provided. Expected type is string, object or callable but type of %s given.",
gettype($handler)
));
}

if (is_null($this->tmpPattern)) {
throw new RuntimeException(sprintf(
"Cannot map handler %s to a pattern. Please use method route before calling method to",
(is_object($handler))? get_class($handler) : (is_string($handler))? $handler : gettype($handler)
));
}

$this->patternMap[] = [$this->tmpPattern => $handler];

$this->tmpPattern = null;

return $this;
}

/**
* @param CommandDispatch $commandDispatch
* @throws \Prooph\ServiceBus\Exception\RuntimeException
*/
public function onRouteCommand(CommandDispatch $commandDispatch)
{
if (is_null($commandDispatch->getCommandName())) {
$commandDispatch->getLogger()->notice(
sprintf("%s: CommandDispatch contains no command name", get_called_class())
);
return;
}

$alreadyMatched = false;

foreach($this->patternMap as $map) {
list($pattern, $handler) = each($map);
if (preg_match($pattern, $commandDispatch->getCommandName())) {

if ($alreadyMatched) {
throw new RuntimeException(sprintf(
"Multiple handlers detected for command %s. The patterns %s and %s matches both",
$commandDispatch->getCommandName(),
$alreadyMatched,
$pattern
));
} else {
$commandDispatch->setCommandHandler($handler);
$alreadyMatched = $pattern;
}
}
}
}

/**
* @param EventDispatch $eventDispatch
*/
public function onRouteEvent(EventDispatch $eventDispatch)
{
if (is_null($eventDispatch->getEventName())) {
$eventDispatch->getLogger()->notice(
sprintf("%s: EventDispatch contains no event name", get_called_class())
);
return;
}

foreach($this->patternMap as $map) {
list($pattern, $handler) = each($map);
if (preg_match($pattern, $eventDispatch->getEventName())) {
$eventDispatch->addEventListener($handler);
}
}
}
}

4 changes: 2 additions & 2 deletions tests/Prooph/ServiceBusTest/Router/CommandRouterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function it_can_handle_routing_definition_by_chaining_route_to()

$commandDispatch->setName(CommandDispatch::ROUTE);

$router->onRouteEvent($commandDispatch);
$router->onRouteCommand($commandDispatch);

$this->assertEquals("DoSomethingHandler", $commandDispatch->getCommandHandler());
}
Expand Down Expand Up @@ -81,7 +81,7 @@ public function it_takes_a_routing_definition_on_instantiation()

$commandDispatch->setName(CommandDispatch::ROUTE);

$router->onRouteEvent($commandDispatch);
$router->onRouteCommand($commandDispatch);

$this->assertEquals("DoSomethingHandler", $commandDispatch->getCommandHandler());
}
Expand Down
Loading

0 comments on commit cf44253

Please sign in to comment.