Completed
Pull Request — master (#24)
by Arnaud
06:14
created

SymfonyExtension::requireKernelBootstrapFile()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 21
rs 9.0534
c 0
b 0
f 0
cc 4
eloc 11
nc 4
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the SymfonyExtension package.
7
 *
8
 * (c) Kamil Kokot <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace FriendsOfBehat\SymfonyExtension\ServiceContainer;
15
16
use Behat\MinkExtension\ServiceContainer\MinkExtension;
17
use Behat\Testwork\EventDispatcher\ServiceContainer\EventDispatcherExtension;
18
use Behat\Testwork\ServiceContainer\Extension;
19
use Behat\Testwork\ServiceContainer\ExtensionManager;
20
use FriendsOfBehat\CrossContainerExtension\CrossContainerProcessor;
21
use FriendsOfBehat\CrossContainerExtension\KernelBasedContainerAccessor;
22
use FriendsOfBehat\CrossContainerExtension\ServiceContainer\CrossContainerExtension;
23
use FriendsOfBehat\SymfonyExtension\Driver\Factory\SymfonyDriverFactory;
24
use FriendsOfBehat\SymfonyExtension\Listener\KernelRebooter;
25
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
26
use Symfony\Component\DependencyInjection\Container;
27
use Symfony\Component\DependencyInjection\ContainerBuilder;
28
use Symfony\Component\DependencyInjection\Definition;
29
use Symfony\Component\DependencyInjection\Reference;
30
use Symfony\Component\Dotenv\Dotenv;
31
32
final class SymfonyExtension implements Extension
33
{
34
    /**
35
     * Kernel used inside Behat contexts or to create services injected to them.
36
     * Container is built before every scenario.
37
     */
38
    const KERNEL_ID = 'sylius_symfony_extension.kernel';
39
40
    /**
41
     * The current container used in scenario contexts.
42
     * To be used as a factory for current injected application services.
43
     */
44
    const KERNEL_CONTAINER_ID = 'sylius_symfony_extension.kernel.container';
45
46
    /**
47
     * Kernel used by Symfony2 driver to isolate web container from contexts' container.
48
     * Container is built before every request.
49
     */
50
    const DRIVER_KERNEL_ID = 'sylius_symfony_extension.driver_kernel';
51
52
    /**
53
     * Kernel that should be used by extensions only.
54
     * Container is built only once at the first use.
55
     */
56
    const SHARED_KERNEL_ID = 'sylius_symfony_extension.shared_kernel';
57
58
    /**
59
     * The only container built by shared kernel.
60
     * To be used as a factory for shared injected application services.
61
     */
62
    const SHARED_KERNEL_CONTAINER_ID = 'sylius_symfony_extension.shared_kernel.container';
63
64
    /**
65
     * @var CrossContainerProcessor|null
66
     */
67
    private $crossContainerProcessor;
68
69
    /**
70
     * {@inheritdoc}
71
     */
72
    public function getConfigKey(): string
73
    {
74
        return 'fob_symfony';
75
    }
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    public function initialize(ExtensionManager $extensionManager): void
81
    {
82
        $this->registerSymfonyDriverFactory($extensionManager);
83
        $this->initializeCrossContainerProcessor($extensionManager);
84
    }
85
86
    /**
87
     * {@inheritdoc}
88
     */
89
    public function configure(ArrayNodeDefinition $builder): void
90
    {
91
        $builder
92
            ->addDefaultsIfNotSet()
93
                ->children()
94
                    ->scalarNode('env_file')->defaultValue('.env')->end()
95
                    ->arrayNode('kernel')
96
                    ->addDefaultsIfNotSet()
97
                        ->children()
98
                            ->scalarNode('path')->defaultValue('app/AppKernel.php')->end()
99
                            ->scalarNode('class')->defaultValue('AppKernel')->end()
100
                            ->booleanNode('debug')->defaultTrue()->end()
101
                        ->end()
102
                    ->end()
103
                ->end()
104
            ->end()
105
        ;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function load(ContainerBuilder $container, array $config): void
112
    {
113
        $this->loadEnvVars($container, $config['env_file']);
114
115
        $environment = getenv('APP_ENV');
116
        $config['kernel']['env'] = $environment ?? 'test';
117
118
        $this->loadKernel($container, $config['kernel']);
119
        $this->loadKernelContainer($container);
120
121
        $this->loadDriverKernel($container);
122
123
        $this->loadSharedKernel($container);
124
        $this->loadSharedKernelContainer($container);
125
126
        $this->loadKernelRebooter($container);
127
        $this->declareSymfonyContainers($container);
128
    }
129
130
    /**
131
     * {@inheritdoc}
132
     */
133
    public function process(ContainerBuilder $container): void
134
    {
135
    }
136
137
    /**
138
     * @param ContainerBuilder $container
139
     * @param string           $fileName
140
     */
141
    private function loadEnvVars(ContainerBuilder $container, string $fileName): void
142
    {
143
        $envFilePath = sprintf('%s/%s', $container->getParameter('paths.base'), $fileName);
144
        (new Dotenv())->load($envFilePath);
145
    }
146
147
    /**
148
     * @param ContainerBuilder $container
149
     */
150
    private function loadKernel(ContainerBuilder $container, array $config): void
151
    {
152
        $definition = new Definition($config['class'], array(
153
            $config['env'],
154
            $config['debug'],
155
        ));
156
        $definition->addMethodCall('boot');
157
        $definition->setFile($this->getKernelFile($container->getParameter('paths.base'), $config['path']));
158
159
        $container->setDefinition(self::KERNEL_ID, $definition);
160
    }
161
162
    /**
163
     * @param ContainerBuilder $container
164
     */
165
    private function loadKernelContainer(ContainerBuilder $container): void
166
    {
167
        $containerDefinition = new Definition(Container::class);
168
        $containerDefinition->setFactory([
169
            new Reference(self::KERNEL_ID),
170
            'getContainer',
171
        ]);
172
173
        $container->setDefinition(self::KERNEL_CONTAINER_ID, $containerDefinition);
174
    }
175
176
    /**
177
     * @param ContainerBuilder $container
178
     */
179
    private function loadDriverKernel(ContainerBuilder $container): void
180
    {
181
        $container->setDefinition(self::DRIVER_KERNEL_ID, $container->findDefinition(self::KERNEL_ID));
182
    }
183
184
    /**
185
     * @param ContainerBuilder $container
186
     */
187
    private function loadSharedKernel(ContainerBuilder $container): void
188
    {
189
        $container->setDefinition(self::SHARED_KERNEL_ID, $container->findDefinition(self::KERNEL_ID));
190
    }
191
192
    /**
193
     * @param ContainerBuilder $container
194
     */
195
    private function loadSharedKernelContainer(ContainerBuilder $container): void
196
    {
197
        $containerDefinition = new Definition(Container::class);
198
        $containerDefinition->setFactory([
199
            new Reference(self::SHARED_KERNEL_ID),
200
            'getContainer',
201
        ]);
202
203
        $container->setDefinition(self::SHARED_KERNEL_CONTAINER_ID, $containerDefinition);
204
    }
205
206
    /**
207
     * @param ContainerBuilder $container
208
     */
209
    private function loadKernelRebooter(ContainerBuilder $container): void
210
    {
211
        $definition = new Definition(KernelRebooter::class, [new Reference(self::KERNEL_ID)]);
212
        $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG);
213
214
        $container->setDefinition(self::KERNEL_ID . '.rebooter', $definition);
215
    }
216
217
    /**
218
     * @param ContainerBuilder $container
219
     *
220
     * @throws \Exception
221
     */
222
    private function declareSymfonyContainers(ContainerBuilder $container): void
223
    {
224
        if (null !== $this->crossContainerProcessor) {
225
            $this->crossContainerProcessor->addContainerAccessor(
226
                'symfony',
227
                new KernelBasedContainerAccessor($container->get(self::KERNEL_ID))
228
            );
229
230
            $this->crossContainerProcessor->addContainerAccessor(
231
                'symfony_driver',
232
                new KernelBasedContainerAccessor($container->get(self::DRIVER_KERNEL_ID))
233
            );
234
235
            $this->crossContainerProcessor->addContainerAccessor(
236
                'symfony_shared',
237
                new KernelBasedContainerAccessor($container->get(self::SHARED_KERNEL_ID))
238
            );
239
        }
240
    }
241
242
    /**
243
     * @param ExtensionManager $extensionManager
244
     */
245
    private function initializeCrossContainerProcessor(ExtensionManager $extensionManager): void
246
    {
247
        /** @var CrossContainerExtension $extension */
248
        $extension = $extensionManager->getExtension('fob_cross_container');
249
        if (null !== $extension) {
250
            $this->crossContainerProcessor = $extension->getCrossContainerProcessor();
251
        }
252
    }
253
254
    /**
255
     * @param ExtensionManager $extensionManager
256
     */
257
    private function registerSymfonyDriverFactory(ExtensionManager $extensionManager): void
258
    {
259
        /** @var MinkExtension $minkExtension */
260
        $minkExtension = $extensionManager->getExtension('mink');
261
        if (null === $minkExtension) {
262
            return;
263
        }
264
265
        $minkExtension->registerDriverFactory(new SymfonyDriverFactory(
266
            'symfony',
267
            new Reference(self::DRIVER_KERNEL_ID)
268
        ));
269
    }
270
271
    /**
272
     * @param string $basePath
273
     * @param string $kernelPath
274
     *
275
     * @return string|null
276
     */
277
    private function getKernelFile(string $basePath, string $kernelPath): ?string
278
    {
279
        $possibleFiles = [
280
            sprintf('%s/%s', $basePath, $kernelPath),
281
            $kernelPath,
282
        ];
283
284
        foreach ($possibleFiles as $possibleFile) {
285
            if (file_exists($possibleFile)) {
286
                return $possibleFile;
287
            }
288
        }
289
290
        return null;
291
    }
292
}
293