Completed
Push — master ( b63fa9...5110b4 )
by Kamil
04:39
created

ResolveCrossContainerReferencesPass::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
3
namespace FriendsOfBehat\CrossContainerExtension;
4
5
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
6
use Symfony\Component\DependencyInjection\ContainerBuilder;
7
use Symfony\Component\DependencyInjection\Definition;
8
use Symfony\Component\DependencyInjection\Reference;
9
10
final class ResolveCrossContainerReferencesPass implements CompilerPassInterface
11
{
12
    /**
13
     * @var ContainerAccessor[]
14
     */
15
    private $containerAccessors;
16
17
    /**
18
     * @param ContainerAccessor[] $containerAccessors
19
     */
20
    public function __construct(array $containerAccessors)
21
    {
22
        $this->containerAccessors = $containerAccessors;
23
    }
24
25
    /**
26
     * {@inheritdoc}
27
     */
28
    public function process(ContainerBuilder $container)
29
    {
30
        foreach ($container->getDefinitions() as $identifier => $definition) {
31
            $container->setDefinition($identifier, $this->resolveDefinition($container, $definition));
32
        }
33
34
        $this->copyParameters($container);
35
    }
36
37
    /**
38
     * @param ContainerBuilder $container
39
     * @param Definition $definition
40
     *
41
     * @return Definition
42
     */
43
    private function resolveDefinition(ContainerBuilder $container, Definition $definition)
44
    {
45
        $definition->setArguments($this->resolveArguments($container, $definition->getArguments()));
46
47
        return $definition;
48
    }
49
50
    /**
51
     * @param ContainerBuilder $container
52
     * @param array $arguments
53
     *
54
     * @return array
55
     */
56
    private function resolveArguments(ContainerBuilder $container, array $arguments)
57
    {
58
        return array_map(function ($argument) use ($container) {
59
            return $this->resolveArgument($container, $argument);
60
        }, $arguments);
61
    }
62
63
    /**
64
     * @param ContainerBuilder $container
65
     * @param mixed $argument
66
     *
67
     * @return mixed
68
     */
69
    private function resolveArgument(ContainerBuilder $container, $argument)
70
    {
71
        if ($argument instanceof Definition) {
72
            return $this->resolveDefinition($container, $argument);
73
        }
74
75
        if ($argument instanceof Reference) {
76
            return $this->resolveReference($container, $argument);
77
        }
78
79
        if (is_array($argument)) {
80
            return $this->resolveArguments($container, $argument);
81
        }
82
83
        return $argument;
84
    }
85
86
    /**
87
     * @param ContainerBuilder $container
88
     * @param Reference $reference
89
     *
90
     * @return Definition|Reference
91
     */
92
    private function resolveReference(ContainerBuilder $container, Reference $reference)
93
    {
94
        if (!ExternalReference::isValid($reference)) {
95
            return $reference;
96
        }
97
98
        return $this->transformReferenceToDefinition($container, new ExternalReference($reference));
99
    }
100
101
    /**
102
     * @param ContainerBuilder $container
103
     * @param ExternalReference $externalReference
104
     *
105
     * @return Definition
106
     */
107
    private function transformReferenceToDefinition(ContainerBuilder $container, ExternalReference $externalReference)
108
    {
109
        $this->assertExternalReferenceHasKnownContainer($externalReference);
110
111
        $containerAccessorIdentifier = sprintf('__%s__', $externalReference->containerIdentifier());
112
        if (!$container->has($containerAccessorIdentifier)) {
113
            $container->set($containerAccessorIdentifier, $this->containerAccessors[$externalReference->containerIdentifier()]);
114
        }
115
116
        $definition = new Definition(null, [$externalReference->serviceIdentifier()]);
117
        $definition->setFactory([new Reference($containerAccessorIdentifier), 'getService']);
118
119
        return $definition;
120
    }
121
122
    /**
123
     * @param ExternalReference $externalReference
124
     *
125
     * @throws \DomainException
126
     */
127
    private function assertExternalReferenceHasKnownContainer(ExternalReference $externalReference)
128
    {
129
        if (!isset($this->containerAccessors[$externalReference->containerIdentifier()])) {
130
            throw new \DomainException(sprintf(
131
                'External container with identifier "%s" does not exist.',
132
                $externalReference->containerIdentifier()
133
            ));
134
        }
135
    }
136
137
    /**
138
     * @param ContainerBuilder $container
139
     */
140
    private function copyParameters(ContainerBuilder $container)
141
    {
142
        foreach ($this->containerAccessors as $containerIdentifier => $containerAccessor) {
143
            foreach ($containerAccessor->getParameters() as $name => $value) {
144
                $container->setParameter(sprintf('__%s__.%s', $containerIdentifier, $name), $value);
145
            }
146
        }
147
    }
148
}
149