Completed
Push — master ( 1a95a1...5ef349 )
by Kamil
04:55
created

CrossContainerProcessor::resolveDefinition()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 2
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
        foreach ($container->getDefinitions() as $identifier => $definition) {
51
            $container->setDefinition($identifier, $this->resolveDefinition($container, $definition));
52
        }
53
54
        $this->copyParameters($container);
55
    }
56
57
    /**
58
     * @param ContainerBuilder $container
59
     * @param Definition $definition
60
     *
61
     * @return Definition
62
     */
63
    private function resolveDefinition(ContainerBuilder $container, Definition $definition)
64
    {
65
        $definition->setArguments($this->resolveArguments($container, $definition->getArguments()));
66
67
        return $definition;
68
    }
69
70
    /**
71
     * @param ContainerBuilder $container
72
     * @param array $arguments
73
     *
74
     * @return array
75
     */
76
    private function resolveArguments(ContainerBuilder $container, array $arguments)
77
    {
78
        return array_map(function ($argument) use ($container) {
79
            return $this->resolveArgument($container, $argument);
80
        }, $arguments);
81
    }
82
83
    /**
84
     * @param ContainerBuilder $container
85
     * @param mixed $argument
86
     *
87
     * @return mixed
88
     */
89
    private function resolveArgument(ContainerBuilder $container, $argument)
90
    {
91
        if ($argument instanceof Definition) {
92
            return $this->resolveDefinition($container, $argument);
93
        }
94
95
        if ($argument instanceof Reference) {
96
            return $this->resolveReference($container, $argument);
97
        }
98
99
        if (is_array($argument)) {
100
            return $this->resolveArguments($container, $argument);
101
        }
102
103
        return $argument;
104
    }
105
106
    /**
107
     * @param ContainerBuilder $container
108
     * @param Reference $reference
109
     *
110
     * @return Definition|Reference
111
     */
112
    private function resolveReference(ContainerBuilder $container, Reference $reference)
113
    {
114
        if (!ExternalReference::isValid($reference)) {
115
            return $reference;
116
        }
117
118
        return $this->transformReferenceToDefinition($container, new ExternalReference($reference));
119
    }
120
121
    /**
122
     * @param ContainerBuilder $container
123
     * @param ExternalReference $externalReference
124
     *
125
     * @return Definition
126
     */
127
    private function transformReferenceToDefinition(ContainerBuilder $container, ExternalReference $externalReference)
128
    {
129
        $this->assertExternalReferenceHasKnownContainer($externalReference);
130
131
        $containerAccessorIdentifier = sprintf('__%s__', $externalReference->containerIdentifier());
132
        if (!$container->has($containerAccessorIdentifier)) {
133
            $container->set($containerAccessorIdentifier, $this->containerAccessors[$externalReference->containerIdentifier()]);
134
        }
135
136
        $definition = new Definition(null, [$externalReference->serviceIdentifier()]);
137
        $definition->setFactory([new Reference($containerAccessorIdentifier), 'getService']);
138
139
        return $definition;
140
    }
141
142
    /**
143
     * @param ExternalReference $externalReference
144
     *
145
     * @throws \DomainException
146
     */
147
    private function assertExternalReferenceHasKnownContainer(ExternalReference $externalReference)
148
    {
149
        if (!isset($this->containerAccessors[$externalReference->containerIdentifier()])) {
150
            throw new \DomainException(sprintf(
151
                'External container with identifier "%s" does not exist.',
152
                $externalReference->containerIdentifier()
153
            ));
154
        }
155
    }
156
157
    /**
158
     * @param ContainerBuilder $container
159
     */
160
    private function copyParameters(ContainerBuilder $container)
161
    {
162
        foreach ($this->containerAccessors as $containerIdentifier => $containerAccessor) {
163
            foreach ($containerAccessor->getParameters() as $name => $value) {
164
                $container->setParameter(sprintf('__%s__.%s', $containerIdentifier, $name), $value);
165
            }
166
        }
167
    }
168
}
169