Skip to content

Instantly share code, notes, and snippets.

@dadamssg
Last active August 29, 2015 14:17
Show Gist options
  • Save dadamssg/06f0c59a7fb35e910325 to your computer and use it in GitHub Desktop.
Save dadamssg/06f0c59a7fb35e910325 to your computer and use it in GitHub Desktop.
<?php
namespace Acme\Project\Infrastructure\AppBundle\Middleware;
use SimpleBus\Message\Handler\MessageHandler;
use SimpleBus\Message\Message;
class CommandHandlerSupportingLazyMiddleware implements MessageHandler
{
/**
* @var string
*/
private $handlerId;
/**
* @var array
*/
private $middlewareIds = [];
/**
* @var callable
*/
private $serviceLocator;
/**
* @param string $handlerId
* @param array $middlewareIds
* @param callable $serviceLocator
*/
public function __construct($handlerId, array $middlewareIds = [], callable $serviceLocator)
{
$this->handlerId = $handlerId;
$this->middlewareIds = $middlewareIds;
$this->serviceLocator = $serviceLocator;
}
/**
* {@inheritdoc}
*/
public function handle(Message $command)
{
call_user_func($this->callableForNextMiddleware(0), $command);
}
private function callableForNextMiddleware($index)
{
$serviceLocator = $this->serviceLocator;
if (!isset($this->middlewareIds[$index])) {
$handlerId = $this->handlerId;
return function(Message $message) use ($handlerId, $serviceLocator) {
$serviceLocator($handlerId)->handle($message);
};
}
$middlewareId = $this->middlewareIds[$index];
return function(Message $message) use ($middlewareId, $serviceLocator, $index) {
$middleware = $serviceLocator($middlewareId);
$middleware->handle($message, $this->callableForNextMiddleware($index + 1));
};
}
}
<?php
namespace Acme\Project\Infrastructure\AppBundle\DependencyInjection\Compiler;
use Acme\Project\Infrastructure\AppBundle\Middleware\CommandHandlerSupportingLazyMiddleware;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
class LazyMiddlewareCompilerPass implements CompilerPassInterface
{
/**
* {@inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has('command_bus')) {
return;
}
$middlewares = $this->deriveLazyMiddlewares($container);
foreach ($middlewares as $handlerId => $middlewareIds) {
$middlewareIds = iterator_to_array($middlewareIds, false);
$this->decorateCommandHandler($handlerId, $middlewareIds, $container);
}
}
/**
* @param ContainerBuilder $container
* @return \SplPriorityQueue[] - an array where keys are primary handler
* id's and values are prioritized queues of
* middleware ids
* @throws \Exception
*/
private function deriveLazyMiddlewares(ContainerBuilder $container)
{
/** @var \SplPriorityQueue[] $middlewares */
$middlewares = [];
$taggedServices = $container->findTaggedServiceIds('command_handler_middleware');
foreach ($taggedServices as $id => $tags) {
foreach ($tags as $tag) {
if (!isset($tag['command_handler'])) {
throw new \Exception(
sprintf("Command handler middleware must define which handler it applies to: %s", $id)
);
}
if (!isset($middlewares[$tag['command_handler']])) {
$middlewares[$tag['command_handler']] = new \SplPriorityQueue();
}
$priority = isset($tag['priority']) ? $tag['priority'] : 0;
$middlewares[$tag['command_handler']]->insert($id, $priority);
}
}
return $middlewares;
}
private function decorateCommandHandler($handlerId, array $middlewareIds, ContainerBuilder $container)
{
$originalId = $handlerId . '.original';
$this->renameDefinition($handlerId, $originalId, $container);
$decoratorDefinition = $container
->register($handlerId, CommandHandlerSupportingLazyMiddleware::CLASS)
->addArgument($originalId)
->addArgument($middlewareIds)
->addArgument(new Reference('simple_bus.command_bus.invokable_service_locator'));
$handlerDefinition = $container->getDefinition($handlerId);
$this->transferTag('command_handler', $handlerDefinition, $decoratorDefinition);
}
private function renameDefinition($oldName, $newName, ContainerBuilder $container)
{
$definition = $container->getDefinition($oldName);
$container->removeDefinition($oldName);
$container->setDefinition($newName, $definition);
}
private function transferTag($name, Definition $from, Definition $to)
{
foreach ($from->getTag($name) as $tag) {
$to->addTag($name, $tag);
}
$from->clearTag($name);
}
}
services:
acme.update_person_command_handler:
class: Acme\Project\Domain\Person\Command\UpdatePersonCommandHandler
arguments:
- @acme.person_repository
tags:
- { name: command_handler, handles: Acme\Project\Domain\Person\Command\UpdatePersonCommand }
acme.update_person_security_authorizor:
class: Acme\Project\Domain\Person\Security\Middleware\UpdatePersonAuthorizer #implements MessageBusMiddleware
arguments:
- @acme.current_user_provider
tags:
- { name: command_handler_middleware, command_handler: acme.update_person_command_handler, priority: 1000 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment