Completed
Push — master ( 2585b9...3ed74c )
by Nikola
02:53
created

CompilerPass::process()   B

Complexity

Conditions 6
Paths 3

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 21.1875

Importance

Changes 0
Metric Value
dl 0
loc 26
ccs 3
cts 12
cp 0.25
rs 8.439
c 0
b 0
f 0
cc 6
eloc 14
nc 3
nop 1
crap 21.1875
1
<?php
2
/*
3
 * This file is part of the  TraitorBundle, an RunOpenCode project.
4
 *
5
 * (c) 2017 RunOpenCode
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace RunOpenCode\Bundle\Traitor\DependencyInjection;
11
12
use RunOpenCode\Bundle\Traitor\Utils\ClassUtils;
13
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
14
use Symfony\Component\DependencyInjection\ContainerBuilder;
15
use Symfony\Component\DependencyInjection\Definition;
16
use Symfony\Component\DependencyInjection\Reference;
17
18
class CompilerPass implements CompilerPassInterface
19
{
20
    /**
21
     * {@inheritdoc}
22
     */
23 7
    public function process(ContainerBuilder $container)
24
    {
25 7
        if (!$container->hasParameter('runopencode.traitor.injectables')) {
26 7
            return;
27
        }
28
29
        if (
30
            $container->hasParameter('runopencode.traitor.filter.tags')
31
            ||
32
            $container->hasParameter('runopencode.traitor.filter.namespaces')
33
        ) {
34
35
            $definitions = array_merge(
36
                $container->hasParameter('runopencode.traitor.filter.tags') ? $this->getDefinitionsFromTags($container, $container->getParameter('runopencode.traitor.filter.tags')) : array(),
37
                $container->hasParameter('runopencode.traitor.filter.namespaces') ? $this->getDefinitionsFromClassNamespaces($container, $container->getParameter('runopencode.traitor.filter.namespaces')) : array()
38
            );
39
40
        } else {
41
42
            $definitions = $this->getDefinitionsFromClassNamespaces($container, array());
43
        }
44
45
        $definitions = $this->filterExcludedDefinitions($container, $definitions);
46
47
        $this->processInjection($definitions, $container->getParameter('runopencode.traitor.injection_map'));
48
    }
49
50
    /**
51
     * Get definitions from container based on namespace filter
52
     *
53
     * @param ContainerBuilder $container
54
     * @param array $filters Namespace prefixes
55
     * @return Definition[] Definitions indexed by service ID
56
     */
57
    protected function getDefinitionsFromClassNamespaces(ContainerBuilder $container, array $filters)
58
    {
59
        $result = array();
60
61
        /**
62
         * @var Definition $definition
63
         */
64
        foreach ($container->getDefinitions() as $id => $definition) {
65
66
            $class = $definition->getClass();
67
68
            if (count($filters) > 0) {
69
70
                $found = false;
71
72
                foreach ($filters as $namespace) {
73
74
                    if (ClassUtils::isWithinNamespace($class, $namespace)) {
75
                        $found = true;
76
                        break;
77
                    }
78
                }
79
80
                if (!$found) {
81
                    continue; // Go to next definition
82
                }
83
            }
84
85
            $result[$id] = $definition;
86
        }
87
88
        return $result;
89
    }
90
91
    /**
92
     * Get definitions from container based on service tag filter
93
     *
94
     * @param ContainerBuilder $container
95
     * @param array $tags Tag names
96
     * @return Definition[] Definitions indexed by service ID
97
     */
98
    protected function getDefinitionsFromTags(ContainerBuilder $container, array $tags)
99
    {
100
        $result = [];
101
102
        if (count($tags) > 0) {
103
            foreach ($tags as $tag) {
104
                foreach (array_keys($container->findTaggedServiceIds($tag)) as $id) {
105
                    $result[$id] = $container->getDefinition($id);
106
                }
107
            }
108
        }
109
110
        return $result;
111
    }
112
113
    /**
114
     * Process injection of services via traits for given definitions.
115
     *
116
     * @param Definition[] $definitions Definitions to process.
117
     * @param array $injectionMap Injection map.
118
     */
119
    protected function processInjection(array $definitions, array $injectionMap)
120
    {
121
        if (count($injectionMap) > 0 && count($definitions) > 0) {
122
123
            /**
124
             * @var Definition $definition
125
             */
126
            foreach ($definitions as $definition) {
127
128
                $class = $definition->getClass();
129
130
                foreach ($injectionMap as $trait => $injectionDefinition) {
131
132
                    if (class_exists($class) && ClassUtils::usesTrait($class, $trait)) {
133
134
                        $arguments = array();
135
136
                        foreach ($injectionDefinition[1] as $argument) {
137
138
                            if ('@' === $argument[0]) {
139
                                $arguments[] = new Reference(ltrim($argument, '@'));
140
                            } else {
141
                                $arguments[] = $argument;
142
                            }
143
                        }
144
145
                        $definition->addMethodCall($injectionDefinition['0'], $arguments);
146
                    }
147
                }
148
            }
149
        }
150
    }
151
152
    /**
153
     * Remove excluded definitions from definitions collection.
154
     *
155
     * @param ContainerBuilder $container
156
     * @param Definition[] $definitions
157
     * @return Definition[]
158
     */
159
    protected function filterExcludedDefinitions(ContainerBuilder $container, array $definitions)
160
    {
161
        $excludedServices = $container->hasParameter('runopencode.traitor.exclude.services') ? $container->getParameter('runopencode.traitor.exclude.services') : array();
162
        $excludedClasses = $container->hasParameter('runopencode.traitor.exclude.classes') ? $container->getParameter('runopencode.traitor.exclude.classes') : array();
163
        $excludedNamespaces = $container->hasParameter('runopencode.traitor.exclude.namespaces') ? $container->getParameter('runopencode.traitor.exclude.namespaces') : array();
164
165
166
        $result = array();
167
168
        foreach ($definitions as $serviceId => $definition) {
169
170
            if (in_array($serviceId, $excludedServices, true)) {
171
                continue;
172
            }
173
174
            $serviceFqcn = ltrim($definition->getClass(), '\\');
175
176
            if (in_array($serviceFqcn, $excludedClasses, true)) {
177
                continue;
178
            }
179
180
            foreach ($excludedNamespaces as $excludedNamespace) {
181
182
                if (strpos($serviceFqcn, $excludedNamespace) === 0) {
183
                    continue 2;
184
                }
185
            }
186
187
            $result[$serviceId] = $definition;
188
        }
189
190
        return $result;
191
    }
192
}
193