Completed
Pull Request — master (#951)
by David
05:59
created

EntityListenerPass::getResolverClass()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 2
nc 2
nop 2
1
<?php
2
3
namespace Doctrine\Bundle\DoctrineBundle\DependencyInjection\Compiler;
4
5
use Doctrine\Bundle\DoctrineBundle\Mapping\ContainerEntityListenerResolver;
6
use Doctrine\Bundle\DoctrineBundle\Mapping\EntityListenerServiceResolver;
7
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
8
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
9
use Symfony\Component\DependencyInjection\ContainerBuilder;
10
use Symfony\Component\DependencyInjection\Definition;
11
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
12
use Symfony\Component\DependencyInjection\Reference;
13
14
/**
15
 * Class for Symfony bundles to register entity listeners
16
 */
17
class EntityListenerPass implements CompilerPassInterface
18
{
19
    /**
20
     * {@inheritDoc}
21
     */
22
    public function process(ContainerBuilder $container)
23
    {
24
        $resolvers = $container->findTaggedServiceIds('doctrine.orm.entity_listener');
25
26
        $lazyServiceReferencesByResolver = [];
27
28
        foreach ($resolvers as $id => $tagAttributes) {
29
            foreach ($tagAttributes as $attributes) {
30
                $name          = isset($attributes['entity_manager']) ? $attributes['entity_manager'] : $container->getParameter('doctrine.default_entity_manager');
31
                $entityManager = sprintf('doctrine.orm.%s_entity_manager', $name);
32
33
                if (! $container->hasDefinition($entityManager)) {
34
                    continue;
35
                }
36
37
                $resolverId = sprintf('doctrine.orm.%s_entity_listener_resolver', $name);
38
39
                if (! $container->has($resolverId)) {
40
                    continue;
41
                }
42
43
                $resolver = $container->findDefinition($resolverId);
44
                $resolver->setPublic(true);
45
46
                if (isset($attributes['entity']) && isset($attributes['event'])) {
47
                    $this->attachToListener($container, $name, $id, $attributes);
48
                }
49
50
                $resolverClass = $this->getResolverClass($resolver, $container);
51
                $resolverSupportsLazyListeners = is_a($resolverClass, EntityListenerServiceResolver::class, true);
52
53
                $lazyByAttribute = isset($attributes['lazy']) && $attributes['lazy'];
54
                if ($lazyByAttribute && ! $resolverSupportsLazyListeners) {
55
                    throw new InvalidArgumentException(sprintf(
56
                        'Lazy-loaded entity listeners can only be resolved by a resolver implementing %s.',
57
                        EntityListenerServiceResolver::class
58
                    ));
59
                }
60
61
                if (! isset($attributes['lazy']) && $resolverSupportsLazyListeners || $lazyByAttribute) {
62
                    $listener = $container->findDefinition($id);
63
64
                    if ($listener->isAbstract()) {
65
                        throw new InvalidArgumentException(sprintf('The service "%s" must not be abstract as this entity listener is lazy-loaded.', $id));
66
                    }
67
68
                    $resolver->addMethodCall('registerService', [$listener->getClass(), $id]);
69
70
                    // if the resolver uses the default class we will use a service locator for all listeners
71
                    if ($resolverClass === ContainerEntityListenerResolver::class) {
72
                        if (! isset($lazyServiceReferencesByResolver[$resolverId])) {
73
                            $lazyServiceReferencesByResolver[$resolverId] = [];
74
                        }
75
                        $lazyServiceReferencesByResolver[$resolverId][$id] = new Reference($id);
76
                    } else {
77
                        $listener->setPublic(true);
78
                    }
79
                } else {
80
                    $resolver->addMethodCall('register', [new Reference($id)]);
81
                }
82
            }
83
        }
84
85
        foreach ($lazyServiceReferencesByResolver as $resolverId => $listenerReferences) {
86
            $container->findDefinition($resolverId)->setArgument(0, ServiceLocatorTagPass::register($container, $listenerReferences));
87
        }
88
    }
89
90
    private function attachToListener(ContainerBuilder $container, $name, $id, array $attributes)
91
    {
92
        $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $name);
93
94
        if (! $container->has($listenerId)) {
95
            return;
96
        }
97
98
        $serviceDef = $container->getDefinition($id);
99
100
        $args = [
101
            $attributes['entity'],
102
            $serviceDef->getClass(),
103
            $attributes['event'],
104
        ];
105
106
        if (isset($attributes['method'])) {
107
            $args[] = $attributes['method'];
108
        }
109
110
        $container->findDefinition($listenerId)->addMethodCall('addEntityListener', $args);
111
    }
112
113
    private function getResolverClass(Definition $resolver, ContainerBuilder $container): string
114
    {
115
        $resolverClass = $resolver->getClass();
116
117
        if (substr($resolverClass, 0, 1) === '%') {
118
            // resolve container parameter first
119
            $resolverClass = $container->getParameterBag()->resolveValue($resolver->getClass());
120
        }
121
122
        return $resolverClass;
123
    }
124
}
125