Commit 3aa4b7f7 authored by Daniel Król's avatar Daniel Król

Event name inferrence

parent ad217e0f
......@@ -8,6 +8,7 @@ namespace Mleko\Narrator\Bundle\DependencyInjection\Compiler;
use Mleko\Narrator\Bundle\Listener\ListenerService;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Reference;
......@@ -17,9 +18,9 @@ class ListenerPass implements \Symfony\Component\DependencyInjection\Compiler\Co
/**
* You can modify the container here before it is dumped to PHP code.
*
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* @param ContainerBuilder $container
*/
public function process(\Symfony\Component\DependencyInjection\ContainerBuilder $container)
public function process(ContainerBuilder $container)
{
$services = $container->findTaggedServiceIds('narrator.listener');
foreach ($services as $serviceId => $tags) {
......@@ -30,22 +31,23 @@ class ListenerPass implements \Symfony\Component\DependencyInjection\Compiler\Co
}
/**
* @param \Symfony\Component\DependencyInjection\ContainerBuilder $container
* @param ContainerBuilder $container
* @param array $tag
* @param string $serviceId
*/
private function processServiceTag(\Symfony\Component\DependencyInjection\ContainerBuilder $container, $tag, $serviceId)
private function processServiceTag(ContainerBuilder $container, $tag, $serviceId)
{
if (!isset($tag['event'])) {
throw new \RuntimeException('The narrator.listener must have event attribute');
$methodName = isset($tag['method']) ? $tag['method'] : "handle";
$eventName = $this->extractEventName($container, $tag, $serviceId, $methodName);
if (null === $eventName) {
throw new UnknownEventName($serviceId);
}
$eventName = $tag['event'];
$busName = isset($tag['bus']) ? $tag['bus'] : 'default';
$methodName = isset($tag['method']) ? $tag['method'] : null;
$this->registerListener($container, $eventName, $busName, $serviceId, $methodName);
}
private function registerListener(\Symfony\Component\DependencyInjection\ContainerBuilder $container, $eventName, $emitterName, $listenerServiceId, $methodName)
private function registerListener(ContainerBuilder $container, $eventName, $emitterName, $listenerServiceId, $methodName)
{
$emitterDefinition = $container->getDefinition('narrator.event_bus.' . $emitterName);
$listeners = $emitterDefinition->getArgument(1);
......@@ -59,4 +61,32 @@ class ListenerPass implements \Symfony\Component\DependencyInjection\Compiler\Co
);
$emitterDefinition->replaceArgument(1, $listeners);
}
private function extractEventName($container, $tag, $serviceId, $methodName)
{
if (!isset($tag['event'])) {
return $this->inferEventName($container, $serviceId, $methodName);
}
return $tag['event'];
}
private function inferEventName(ContainerBuilder $container, $serviceId, $methodName)
{
$listenerClass = $container->getDefinition($serviceId)->getClass();
if (null === $listenerClass) {
return null;
}
$reflectionMethod = new \ReflectionMethod($listenerClass, $methodName);
$parameters = $reflectionMethod->getParameters();
if (count($parameters) < 1) {
return null;
}
$firstParameter = $parameters[0];
$firstParameterType = $firstParameter->getType();
if (null === $firstParameterType) {
return null;
}
return (string)$firstParameterType;
}
}
<?php
namespace Mleko\Narrator\Bundle\DependencyInjection\Compiler;
class UnknownEventName extends \RuntimeException
{
/**
* UnknownEventName constructor.
* @param string $serviceId
*/
public function __construct($serviceId)
{
parent::__construct(sprintf("Unknown event name for listener: `%s`, `narrator.listener` tag don't have an `event` attribute and event name could't be inferred", $serviceId));
}
}
......@@ -6,55 +6,88 @@
namespace Mleko\Narrator\Bundle\Tests\DependencyInjection\Compiler\Listener;
use Mleko\Narrator\Bundle\DependencyInjection\Compiler\UnknownEventName;
use Mleko\Narrator\Bundle\Tests\Integration\TestApp\Counter;
use Mleko\Narrator\Bundle\Tests\Integration\TestApp\StdCounter;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Definition;
class ListenerPassTest extends \PHPUnit_Framework_TestCase
{
/** @var \Symfony\Component\DependencyInjection\ContainerBuilder | \PHPUnit_Framework_MockObject_MockObject */
/** @var ContainerBuilder | \PHPUnit_Framework_MockObject_MockObject */
private $container;
/** @var \Mleko\Narrator\Bundle\DependencyInjection\Compiler\ListenerPass */
private $compilerPass;
/** @var \Symfony\Component\DependencyInjection\Definition */
/** @var Definition */
private $defaultEmitterDefinition;
public function setUp()
{
$this->container = $this->getMockBuilder(\Symfony\Component\DependencyInjection\ContainerBuilder::class)->getMock();
$this->container = $this->getMockBuilder(ContainerBuilder::class)->getMock();
$this->container = new ContainerBuilder();
$this->defaultEmitterDefinition = new Definition(null, [null, []]);
$this->defaultEmitterDefinition = new \Symfony\Component\DependencyInjection\Definition(null, [null, []]);
$this->container->method('getDefinition')->with('narrator.event_bus.default')->willReturn($this->defaultEmitterDefinition);
$this->container->setDefinition('narrator.event_bus.default', $this->defaultEmitterDefinition);
$this->compilerPass = new \Mleko\Narrator\Bundle\DependencyInjection\Compiler\ListenerPass();
}
public function testProcess()
{
$this->container->expects($this->once())->method('findTaggedServiceIds')
->willReturn([
'service1' => [
[
'event' => 'stdClass'
]
]
]);
$listenerDefinition = new Definition();
$listenerDefinition->addTag("narrator.listener", ["event" => \stdClass::class]);
$this->container->setDefinition("service1", $listenerDefinition);
$this->compilerPass->process($this->container);
$listeners = $this->defaultEmitterDefinition->getArgument(1);
$this->assertCount(1, $listeners);
$this->assertCount(1, $listeners[\stdClass::class]);
}
public function testFailOnMissingEventName()
{
$this->container->expects($this->once())->method('findTaggedServiceIds')
->willReturn([
'service1' => [
[]
]
]);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('The narrator.listener must have event attribute');
$listenerDefinition = new Definition();
$listenerDefinition->addTag("narrator.listener");
$this->container->setDefinition("service1", $listenerDefinition);
$this->expectException(UnknownEventName::class);
$this->compilerPass->process($this->container);
}
public function testEventClassInferring()
{
$counterDefinition = new Definition(StdCounter::class);
$counterDefinition->addTag("narrator.listener");
$this->container->setDefinition("counter", $counterDefinition);
$this->compilerPass->process($this->container);
$listeners = $this->defaultEmitterDefinition->getArgument(1);
$this->assertCount(1, $listeners[\stdClass::class]);
}
public function testUntypedEventHandlerClassInferring()
{
$counterDefinition = new Definition(Counter::class);
$counterDefinition->addTag("narrator.listener");
$this->container->setDefinition("counter", $counterDefinition);
$this->expectException(UnknownEventName::class);
$this->compilerPass->process($this->container);
}
public function testParameterLessEventHandlerClassInferring()
{
$counterDefinition = new Definition(StdCounter::class);
$counterDefinition->addTag("narrator.listener", ["method" => "handleParameterLess"]);
$this->container->setDefinition("counter", $counterDefinition);
$this->expectException(UnknownEventName::class);
$this->compilerPass->process($this->container);
}
......
<?php
namespace Mleko\Narrator\Bundle\Tests\Integration\TestApp;
class StdCounter
{
private $count = 0;
private $lastEvent;
public function handle(\stdClass $event)
{
$this->count++;
$this->lastEvent = $event;
}
public function handleParameterLess()
{
$this->count++;
}
public function getCount()
{
return $this->count;
}
public function getLastEvent()
{
return $this->lastEvent;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment