Completed
Pull Request — master (#9)
by Kamil
02:26 queued 18s
created

CrossContainerProcessor   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 172
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 4

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 4
dl 0
loc 172
rs 10
c 0
b 0
f 0

12 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 6 2
A addContainerAccessor() 0 4 1
A process() 0 8 1
A resolveDefinition() 0 8 1
A resolveFactory() 0 12 4
A resolveMethodCalls() 0 6 1
A resolveArguments() 0 6 1
A resolveArgument() 0 16 4
A resolveReference() 0 8 2
A transformReferenceToDefinition() 0 9 1
A assertExternalReferenceHasKnownContainer() 0 9 2
A copyParameters() 0 8 3
1
<?php
2
3
/*
4
 * This file is part of the CrossContainerExtension package.
5
 *
6
 * (c) Kamil Kokot <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FriendsOfBehat\CrossContainerExtension;
13
14
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
15
use Symfony\Component\DependencyInjection\ContainerBuilder;
16
use Symfony\Component\DependencyInjection\Definition;
17
use Symfony\Component\DependencyInjection\Reference;
18
19
final class CrossContainerProcessor implements CompilerPassInterface
20
{
21
    /**
22
     * @var ContainerAccessor[]
23
     */
24
    private $containerAccessors;
25
26
    /**
27
     * @param ContainerAccessor[] $containerAccessors
28
     */
29
    public function __construct(array $containerAccessors = [])
30
    {
31
        foreach ($containerAccessors as $containerIdentifier => $containerAccessor) {
32
            $this->addContainerAccessor($containerIdentifier, $containerAccessor);
33
        }
34
    }
35
36
    /**
37
     * @param string $containerIdentifier
38
     * @param ContainerAccessor $containerAccessor
39
     */
40
    public function addContainerAccessor($containerIdentifier, ContainerAccessor $containerAccessor)
41
    {
42
        $this->containerAccessors[$containerIdentifier] = $containerAccessor;
43
    }
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function process(ContainerBuilder $container)
49
    {
50
        $container->addDefinitions(array_map(function (Definition $definition) {
51
            return $this->resolveDefinition($definition);
52
        }, $container->getDefinitions()));
53
54
        $this->copyParameters($container);
55
    }
56
57
    /**
58
     * @param Definition $definition
59
     *
60
     * @return Definition
61
     */
62
    private function resolveDefinition(Definition $definition)
63
    {
64
        $definition->setArguments($this->resolveArguments($definition->getArguments()));
65
        $definition->setFactory($this->resolveFactory($definition->getFactory()));
0 ignored issues
show
Bug introduced by
It seems like $definition->getFactory() targeting Symfony\Component\Depend...efinition::getFactory() can also be of type string; however, FriendsOfBehat\CrossCont...essor::resolveFactory() does only seem to accept array|null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $this->resolveFactory($definition->getFactory()) targeting FriendsOfBehat\CrossCont...essor::resolveFactory() can also be of type null; however, Symfony\Component\Depend...efinition::setFactory() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
66
        $definition->setMethodCalls($this->resolveMethodCalls($definition->getMethodCalls()));
67
68
        return $definition;
69
    }
70
71
    /**
72
     * @param array|null $factory
73
     *
74
     * @return array|null
75
     */
76
    private function resolveFactory($factory)
77
    {
78
        if ([] === $factory) {
79
            return [];
80
        }
81
82
        if (isset($factory[0]) && $factory[0] instanceof Reference) {
83
            $factory[0] = $this->resolveReference($factory[0]);
84
        }
85
86
        return $factory;
87
    }
88
89
    /**
90
     * @param array $methodCalls
91
     *
92
     * @return array
93
     */
94
    private function resolveMethodCalls(array $methodCalls)
95
    {
96
        return array_map(function (array $methodCall) {
97
            return [$methodCall[0], $this->resolveArguments($methodCall[1])];
98
        }, $methodCalls);
99
    }
100
101
    /**
102
     * @param array $arguments
103
     *
104
     * @return array
105
     */
106
    private function resolveArguments(array $arguments)
107
    {
108
        return array_map(function ($argument) {
109
            return $this->resolveArgument($argument);
110
        }, $arguments);
111
    }
112
113
    /**
114
     * @param mixed $argument
115
     *
116
     * @return mixed
117
     */
118
    private function resolveArgument($argument)
119
    {
120
        if ($argument instanceof Definition) {
121
            return $this->resolveDefinition($argument);
122
        }
123
124
        if ($argument instanceof Reference) {
125
            return $this->resolveReference($argument);
126
        }
127
128
        if (is_array($argument)) {
129
            return $this->resolveArguments($argument);
130
        }
131
132
        return $argument;
133
    }
134
135
    /**
136
     * @param Reference $reference
137
     *
138
     * @return Definition|Reference
139
     */
140
    private function resolveReference(Reference $reference)
141
    {
142
        if (!ExternalReference::isValid($reference)) {
143
            return $reference;
144
        }
145
146
        return $this->transformReferenceToDefinition(new ExternalReference($reference));
147
    }
148
149
    /**
150
     * @param ExternalReference $externalReference
151
     *
152
     * @return Definition
153
     */
154
    private function transformReferenceToDefinition(ExternalReference $externalReference)
155
    {
156
        $this->assertExternalReferenceHasKnownContainer($externalReference);
157
158
        $definition = new Definition(null, [$externalReference->serviceIdentifier()]);
159
        $definition->setFactory([$this->containerAccessors[$externalReference->containerIdentifier()], 'getService']);
160
161
        return $definition;
162
    }
163
164
    /**
165
     * @param ExternalReference $externalReference
166
     *
167
     * @throws \DomainException
168
     */
169
    private function assertExternalReferenceHasKnownContainer(ExternalReference $externalReference)
170
    {
171
        if (!isset($this->containerAccessors[$externalReference->containerIdentifier()])) {
172
            throw new \DomainException(sprintf(
173
                'External container with identifier "%s" does not exist.',
174
                $externalReference->containerIdentifier()
175
            ));
176
        }
177
    }
178
179
    /**
180
     * @param ContainerBuilder $container
181
     */
182
    private function copyParameters(ContainerBuilder $container)
183
    {
184
        foreach ($this->containerAccessors as $containerIdentifier => $containerAccessor) {
185
            foreach ($containerAccessor->getParameters() as $name => $value) {
186
                $container->setParameter(sprintf('__%s__.%s', $containerIdentifier, $name), $value);
187
            }
188
        }
189
    }
190
}
191