Skip to content

Commit

Permalink
Fix for multiple tags
Browse files Browse the repository at this point in the history
  • Loading branch information
HypeMC committed Oct 8, 2024
1 parent 6f6130a commit 264cf5d
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 36 deletions.
72 changes: 42 additions & 30 deletions DependencyInjection/Compiler/AddProcessorsPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

namespace Symfony\Bundle\MonologBundle\DependencyInjection\Compiler;

use Symfony\Component\DependencyInjection\Argument\TaggedIteratorArgument;
use Symfony\Component\DependencyInjection\ChildDefinition;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
Expand All @@ -33,43 +34,54 @@ public function process(ContainerBuilder $container)
return;
}

$indexedTags = [];
$i = 1;

foreach ($container->findTaggedServiceIds('monolog.processor') as $id => $tags) {
foreach ($tags as &$tag) {
$indexedTags[$tag['index'] = $i++] = $tag;
}
unset($tag);
$definition = $container->getDefinition($id);
$definition->setTags(array_merge($definition->getTags(), ['monolog.processor' => $tags]));
}

$taggedIteratorArgument = new TaggedIteratorArgument('monolog.processor', 'index', null, true);
// array_reverse is used because ProcessableHandlerTrait::pushProcessor prepends processors to the beginning of the stack
foreach (array_reverse($this->findAndSortTaggedServices('monolog.processor', $container)) as $reference) {
$tags = $container->getDefinition((string) $reference)->getTag('monolog.processor');
foreach (array_reverse($this->findAndSortTaggedServices($taggedIteratorArgument, $container), true) as $index => $reference) {
$tag = $indexedTags[$index];

foreach ($tags as $tag) {
if (!empty($tag['channel']) && !empty($tag['handler'])) {
throw new \InvalidArgumentException(\sprintf('you cannot specify both the "handler" and "channel" attributes for the "monolog.processor" tag on service "%s"', $reference));
}
if (!empty($tag['channel']) && !empty($tag['handler'])) {
throw new \InvalidArgumentException(\sprintf('you cannot specify both the "handler" and "channel" attributes for the "monolog.processor" tag on service "%s"', $reference));
}

if (!empty($tag['handler'])) {
$definition = $container->findDefinition(\sprintf('monolog.handler.%s', $tag['handler']));
$parentDef = $definition;
while (!$parentDef->getClass() && $parentDef instanceof ChildDefinition) {
$parentDef = $container->findDefinition($parentDef->getParent());
}
$class = $container->getParameterBag()->resolveValue($parentDef->getClass());
if (!method_exists($class, 'pushProcessor')) {
throw new \InvalidArgumentException(\sprintf('The "%s" handler does not accept processors', $tag['handler']));
}
} elseif (!empty($tag['channel'])) {
if ('app' === $tag['channel']) {
$definition = $container->getDefinition('monolog.logger');
} else {
$definition = $container->getDefinition(\sprintf('monolog.logger.%s', $tag['channel']));
}
} else {
$definition = $container->getDefinition('monolog.logger_prototype');
if (!empty($tag['handler'])) {
$definition = $container->findDefinition(\sprintf('monolog.handler.%s', $tag['handler']));
$parentDef = $definition;
while (!$parentDef->getClass() && $parentDef instanceof ChildDefinition) {
$parentDef = $container->findDefinition($parentDef->getParent());
}

if (!empty($tag['method'])) {
$processor = [$reference, $tag['method']];
$class = $container->getParameterBag()->resolveValue($parentDef->getClass());
if (!method_exists($class, 'pushProcessor')) {
throw new \InvalidArgumentException(\sprintf('The "%s" handler does not accept processors', $tag['handler']));
}
} elseif (!empty($tag['channel'])) {
if ('app' === $tag['channel']) {
$definition = $container->getDefinition('monolog.logger');
} else {
// If no method is defined, fallback to use __invoke
$processor = $reference;
$definition = $container->getDefinition(\sprintf('monolog.logger.%s', $tag['channel']));
}
$definition->addMethodCall('pushProcessor', [$processor]);
} else {
$definition = $container->getDefinition('monolog.logger_prototype');
}

if (!empty($tag['method'])) {
$processor = [$reference, $tag['method']];
} else {
// If no method is defined, fallback to use __invoke
$processor = $reference;
}
$definition->addMethodCall('pushProcessor', [$processor]);
}
}
}
34 changes: 28 additions & 6 deletions Tests/DependencyInjection/Compiler/AddProcessorsPassTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
use Symfony\Component\DependencyInjection\TypedReference;

class AddProcessorsPassTest extends TestCase
{
Expand All @@ -31,19 +32,28 @@ public function testHandlerProcessors()
$service = $container->getDefinition('monolog.handler.test');
$calls = $service->getMethodCalls();
$this->assertCount(1, $calls);
$this->assertEquals(['pushProcessor', [new Reference('test')]], $calls[0]);
$this->assertEquals(['pushProcessor', [new TypedReference('test', 'TestClass')]], $calls[0]);

$service = $container->getDefinition('handler_test');
$calls = $service->getMethodCalls();
$this->assertCount(1, $calls);
$this->assertEquals(['pushProcessor', [new Reference('test2')]], $calls[0]);
$this->assertEquals(['pushProcessor', [new TypedReference('test2', 'TestClass')]], $calls[0]);

$service = $container->getDefinition('monolog.handler.priority_test');
$calls = $service->getMethodCalls();
$this->assertCount(3, $calls);
$this->assertEquals(['pushProcessor', [new Reference('processor-10')]], $calls[0]);
$this->assertEquals(['pushProcessor', [new Reference('processor+10')]], $calls[1]);
$this->assertEquals(['pushProcessor', [new Reference('processor+20')]], $calls[2]);
$this->assertCount(5, $calls);
$this->assertEquals(['pushProcessor', [new TypedReference('processor-10', 'TestClass')]], $calls[0]);
$this->assertEquals(['pushProcessor', [new TypedReference('processor+10', 'TestClass')]], $calls[1]);
$this->assertEquals(['pushProcessor', [new TypedReference('processor+20', 'TestClass')]], $calls[2]);
$this->assertEquals(['pushProcessor', [new TypedReference('processor+20', 'TestClass')]], $calls[2]);
$this->assertEquals(['pushProcessor', [new TypedReference('processor+25+35', 'TestClass')]], $calls[3]);
$this->assertEquals(['pushProcessor', [new TypedReference('processor+35+25', 'TestClass')]], $calls[4]);

$service = $container->getDefinition('monolog.handler.priority_test_2');
$calls = $service->getMethodCalls();
$this->assertCount(2, $calls);
$this->assertEquals(['pushProcessor', [new TypedReference('processor+35+25', 'TestClass')]], $calls[0]);
$this->assertEquals(['pushProcessor', [new TypedReference('processor+25+35', 'TestClass')]], $calls[1]);
}

public function testFailureOnHandlerWithoutPushProcessor()
Expand Down Expand Up @@ -83,10 +93,12 @@ protected function getContainer()
$container->setDefinition('monolog.handler.test', new Definition('%monolog.handler.console.class%', [100, false]));
$container->setDefinition('handler_test', new Definition('%monolog.handler.console.class%', [100, false]));
$container->setDefinition('monolog.handler.priority_test', new Definition('%monolog.handler.console.class%', [100, false]));
$container->setDefinition('monolog.handler.priority_test_2', new Definition('%monolog.handler.console.class%', [100, false]));
$container->setAlias('monolog.handler.test2', 'handler_test');
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.test')]);
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.test2')]);
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.priority_test')]);
$definition->addMethodCall('pushHandler', [new Reference('monolog.handler.priority_test_2')]);

$service = new Definition('TestClass', ['false', new Reference('logger')]);
$service->addTag('monolog.processor', ['handler' => 'test']);
Expand All @@ -108,6 +120,16 @@ protected function getContainer()
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 20]);
$container->setDefinition('processor+20', $service);

$service = new Definition('TestClass', ['false', new Reference('logger')]);
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 35]);
$service->addTag('monolog.processor', ['handler' => 'priority_test_2', 'priority' => 25]);
$container->setDefinition('processor+35+25', $service);

$service = new Definition('TestClass', ['false', new Reference('logger')]);
$service->addTag('monolog.processor', ['handler' => 'priority_test', 'priority' => 25]);
$service->addTag('monolog.processor', ['handler' => 'priority_test_2', 'priority' => 35]);
$container->setDefinition('processor+25+35', $service);

$container->getCompilerPassConfig()->setOptimizationPasses([]);
$container->getCompilerPassConfig()->setRemovingPasses([]);
$container->addCompilerPass(new AddProcessorsPass());
Expand Down

0 comments on commit 264cf5d

Please sign in to comment.