Completed
Push — master ( e56cb3...ac31f3 )
by Andreas
01:49 queued 11s
created

EntityListenerPass   A

Complexity

Total Complexity 28

Size/Duplication

Total Lines 126
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 28
lcom 1
cbo 8
dl 0
loc 126
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A attachToListener() 0 22 5
A getResolverClass() 0 11 2
A getConcreteDefinitionClass() 0 18 4
C process() 0 64 17
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\ChildDefinition;
8
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
9
use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait;
10
use Symfony\Component\DependencyInjection\Compiler\ServiceLocatorTagPass;
11
use Symfony\Component\DependencyInjection\ContainerBuilder;
12
use Symfony\Component\DependencyInjection\Definition;
13
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
14
use Symfony\Component\DependencyInjection\Reference;
15
16
/**
17
 * Class for Symfony bundles to register entity listeners
18
 */
19
class EntityListenerPass implements CompilerPassInterface
20
{
21
    use PriorityTaggedServiceTrait;
22
23
    /**
24
     * {@inheritDoc}
25
     */
26
    public function process(ContainerBuilder $container)
27
    {
28
        $resolvers = $this->findAndSortTaggedServices('doctrine.orm.entity_listener', $container);
29
30
        $lazyServiceReferencesByResolver = [];
31
32
        foreach ($resolvers as $reference) {
33
            $id = $reference->__toString();
34
            foreach ($container->getDefinition($id)->getTag('doctrine.orm.entity_listener') as $attributes) {
35
                $name          = isset($attributes['entity_manager']) ? $attributes['entity_manager'] : $container->getParameter('doctrine.default_entity_manager');
36
                $entityManager = sprintf('doctrine.orm.%s_entity_manager', $name);
37
38
                if (! $container->hasDefinition($entityManager)) {
39
                    continue;
40
                }
41
42
                $resolverId = sprintf('doctrine.orm.%s_entity_listener_resolver', $name);
43
44
                if (! $container->has($resolverId)) {
45
                    continue;
46
                }
47
48
                $resolver = $container->findDefinition($resolverId);
49
                $resolver->setPublic(true);
50
51
                if (isset($attributes['entity']) && isset($attributes['event'])) {
52
                    $this->attachToListener($container, $name, $this->getConcreteDefinitionClass($container->findDefinition($id), $container, $id), $attributes);
53
                }
54
55
                $resolverClass                 = $this->getResolverClass($resolver, $container, $resolverId);
56
                $resolverSupportsLazyListeners = is_a($resolverClass, EntityListenerServiceResolver::class, true);
57
58
                $lazyByAttribute = isset($attributes['lazy']) && $attributes['lazy'];
59
                if ($lazyByAttribute && ! $resolverSupportsLazyListeners) {
60
                    throw new InvalidArgumentException(sprintf(
61
                        'Lazy-loaded entity listeners can only be resolved by a resolver implementing %s.',
62
                        EntityListenerServiceResolver::class
63
                    ));
64
                }
65
66
                if (! isset($attributes['lazy']) && $resolverSupportsLazyListeners || $lazyByAttribute) {
67
                    $listener = $container->findDefinition($id);
68
69
                    $resolver->addMethodCall('registerService', [$this->getConcreteDefinitionClass($listener, $container, $id), $id]);
70
71
                    // if the resolver uses the default class we will use a service locator for all listeners
72
                    if ($resolverClass === ContainerEntityListenerResolver::class) {
73
                        if (! isset($lazyServiceReferencesByResolver[$resolverId])) {
74
                            $lazyServiceReferencesByResolver[$resolverId] = [];
75
                        }
76
                        $lazyServiceReferencesByResolver[$resolverId][$id] = new Reference($id);
77
                    } else {
78
                        $listener->setPublic(true);
79
                    }
80
                } else {
81
                    $resolver->addMethodCall('register', [new Reference($id)]);
82
                }
83
            }
84
        }
85
86
        foreach ($lazyServiceReferencesByResolver as $resolverId => $listenerReferences) {
87
            $container->findDefinition($resolverId)->setArgument(0, ServiceLocatorTagPass::register($container, $listenerReferences));
88
        }
89
    }
90
91
    private function attachToListener(ContainerBuilder $container, string $name, string $class, array $attributes) : void
92
    {
93
        $listenerId = sprintf('doctrine.orm.%s_listeners.attach_entity_listeners', $name);
94
95
        if (! $container->has($listenerId)) {
96
            return;
97
        }
98
99
        $args = [
100
            $attributes['entity'],
101
            $class,
102
            $attributes['event'],
103
        ];
104
105
        if (isset($attributes['method'])) {
106
            $args[] = $attributes['method'];
107
        } elseif (! method_exists($class, $attributes['event']) && method_exists($class, '__invoke')) {
108
            $args[] = '__invoke';
109
        }
110
111
        $container->findDefinition($listenerId)->addMethodCall('addEntityListener', $args);
112
    }
113
114
    private function getResolverClass(Definition $resolver, ContainerBuilder $container, string $id) : string
115
    {
116
        $resolverClass = $this->getConcreteDefinitionClass($resolver, $container, $id);
117
118
        if (substr($resolverClass, 0, 1) === '%') {
119
            // resolve container parameter first
120
            $resolverClass = $container->getParameterBag()->resolveValue($resolverClass);
121
        }
122
123
        return $resolverClass;
124
    }
125
126
    private function getConcreteDefinitionClass(Definition $definition, ContainerBuilder $container, string $id) : string
127
    {
128
        $class = $definition->getClass();
129
        if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
130
            return $class;
131
        }
132
133
        while ($definition instanceof ChildDefinition) {
134
            $definition = $container->findDefinition($definition->getParent());
135
136
            $class = $definition->getClass();
137
            if ($class) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $class of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
138
                return $class;
139
            }
140
        }
141
142
        throw new InvalidArgumentException(sprintf('The service "%s" must define its class.', $id));
143
    }
144
}
145