Completed
Pull Request — master (#24)
by Arnaud
01:27
created

SymfonyExtension::loadEnvVars()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
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')->defaultNull()->end()
95
                    ->arrayNode('kernel')
96
                    ->addDefaultsIfNotSet()
97
                        ->children()
98
                            ->scalarNode('bootstrap')->defaultValue('app/autoload.php')->end()
99
                            ->scalarNode('path')->defaultValue('app/AppKernel.php')->end()
100
                            ->scalarNode('class')->defaultValue('AppKernel')->end()
101
                            ->scalarNode('env')->defaultValue('test')->end()
102
                            ->booleanNode('debug')->defaultTrue()->end()
103
                        ->end()
104
                    ->end()
105
                ->end()
106
            ->end()
107
        ;
108
    }
109
110
    /**
111
     * {@inheritdoc}
112
     */
113
    public function load(ContainerBuilder $container, array $config): void
114
    {
115
        if (null !== $config['env_file']) {
116
            $this->loadEnvVars($container, $config['env_file']);
117
118
            $environment = getenv('APP_ENV');
119
            $config['kernel']['env'] = $environment ?? 'test';
120
        } else {
121
            $this->requireKernelBootstrapFile($container->getParameter('paths.base'), $config['bootstrap']);
122
        }
123
124
        $this->loadKernel($container, $config['kernel']);
125
        $this->loadKernelContainer($container);
126
127
        $this->loadDriverKernel($container);
128
129
        $this->loadSharedKernel($container);
130
        $this->loadSharedKernelContainer($container);
131
132
        $this->loadKernelRebooter($container);
133
        $this->declareSymfonyContainers($container);
134
    }
135
136
    /**
137
     * {@inheritdoc}
138
     */
139
    public function process(ContainerBuilder $container): void
140
    {
141
    }
142
143
    /**
144
     * @param ContainerBuilder $container
145
     * @param string           $fileName
146
     */
147
    private function loadEnvVars(ContainerBuilder $container, string $fileName): void
148
    {
149
        $envFilePath = sprintf('%s/%s', $container->getParameter('paths.base'), $fileName);
150
        (new Dotenv())->load($envFilePath);
151
    }
152
153
    /**
154
     * @param ContainerBuilder $container
155
     */
156
    private function loadKernel(ContainerBuilder $container, array $config): void
157
    {
158
        $definition = new Definition($config['class'], array(
159
            $config['env'],
160
            $config['debug'],
161
        ));
162
        $definition->addMethodCall('boot');
163
        $definition->setFile($this->getKernelFile($container->getParameter('paths.base'), $config['path']));
164
165
        $container->setDefinition(self::KERNEL_ID, $definition);
166
    }
167
168
    /**
169
     * @param ContainerBuilder $container
170
     */
171
    private function loadKernelContainer(ContainerBuilder $container): void
172
    {
173
        $containerDefinition = new Definition(Container::class);
174
        $containerDefinition->setFactory([
175
            new Reference(self::KERNEL_ID),
176
            'getContainer',
177
        ]);
178
179
        $container->setDefinition(self::KERNEL_CONTAINER_ID, $containerDefinition);
180
    }
181
182
    /**
183
     * @param ContainerBuilder $container
184
     */
185
    private function loadDriverKernel(ContainerBuilder $container): void
186
    {
187
        $container->setDefinition(self::DRIVER_KERNEL_ID, $container->findDefinition(self::KERNEL_ID));
188
    }
189
190
    /**
191
     * @param ContainerBuilder $container
192
     */
193
    private function loadSharedKernel(ContainerBuilder $container): void
194
    {
195
        $container->setDefinition(self::SHARED_KERNEL_ID, $container->findDefinition(self::KERNEL_ID));
196
    }
197
198
    /**
199
     * @param ContainerBuilder $container
200
     */
201
    private function loadSharedKernelContainer(ContainerBuilder $container): void
202
    {
203
        $containerDefinition = new Definition(Container::class);
204
        $containerDefinition->setFactory([
205
            new Reference(self::SHARED_KERNEL_ID),
206
            'getContainer',
207
        ]);
208
209
        $container->setDefinition(self::SHARED_KERNEL_CONTAINER_ID, $containerDefinition);
210
    }
211
212
    /**
213
     * @param ContainerBuilder $container
214
     */
215
    private function loadKernelRebooter(ContainerBuilder $container): void
216
    {
217
        $definition = new Definition(KernelRebooter::class, [new Reference(self::KERNEL_ID)]);
218
        $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG);
219
220
        $container->setDefinition(self::KERNEL_ID . '.rebooter', $definition);
221
    }
222
223
    /**
224
     * @param ContainerBuilder $container
225
     *
226
     * @throws \Exception
227
     */
228
    private function declareSymfonyContainers(ContainerBuilder $container): void
229
    {
230
        if (null !== $this->crossContainerProcessor) {
231
            $this->crossContainerProcessor->addContainerAccessor(
232
                'symfony',
233
                new KernelBasedContainerAccessor($container->get(self::KERNEL_ID))
234
            );
235
236
            $this->crossContainerProcessor->addContainerAccessor(
237
                'symfony_driver',
238
                new KernelBasedContainerAccessor($container->get(self::DRIVER_KERNEL_ID))
239
            );
240
241
            $this->crossContainerProcessor->addContainerAccessor(
242
                'symfony_shared',
243
                new KernelBasedContainerAccessor($container->get(self::SHARED_KERNEL_ID))
244
            );
245
        }
246
    }
247
248
    /**
249
     * @param ExtensionManager $extensionManager
250
     */
251
    private function initializeCrossContainerProcessor(ExtensionManager $extensionManager): void
252
    {
253
        /** @var CrossContainerExtension $extension */
254
        $extension = $extensionManager->getExtension('fob_cross_container');
255
        if (null !== $extension) {
256
            $this->crossContainerProcessor = $extension->getCrossContainerProcessor();
257
        }
258
    }
259
260
    /**
261
     * @param ExtensionManager $extensionManager
262
     */
263
    private function registerSymfonyDriverFactory(ExtensionManager $extensionManager): void
264
    {
265
        /** @var MinkExtension $minkExtension */
266
        $minkExtension = $extensionManager->getExtension('mink');
267
        if (null === $minkExtension) {
268
            return;
269
        }
270
271
        $minkExtension->registerDriverFactory(new SymfonyDriverFactory(
272
            'symfony',
273
            new Reference(self::DRIVER_KERNEL_ID)
274
        ));
275
    }
276
277
    /**
278
     * @param string $basePath
279
     * @param string $kernelPath
280
     *
281
     * @return string|null
282
     */
283
    private function getKernelFile(string $basePath, string $kernelPath): ?string
284
    {
285
        $possibleFiles = [
286
            sprintf('%s/%s', $basePath, $kernelPath),
287
            $kernelPath,
288
        ];
289
290
        foreach ($possibleFiles as $possibleFile) {
291
            if (file_exists($possibleFile)) {
292
                return $possibleFile;
293
            }
294
        }
295
296
        return null;
297
    }
298
299
    /**
300
     * @param string $basePath
301
     * @param string|null $bootstrapPath
302
     *
303
     * @throws \DomainException
304
     */
305
    private function requireKernelBootstrapFile(string $basePath, ?string $bootstrapPath): void
306
    {
307
        if (null === $bootstrapPath) {
308
            return;
309
        }
310
311
        $possiblePaths = [
312
            sprintf('%s/%s', $basePath, $bootstrapPath),
313
            $bootstrapPath,
314
        ];
315
316
        foreach ($possiblePaths as $possiblePath) {
317
            if (file_exists($possiblePath)) {
318
                require_once $possiblePath;
319
320
                return;
321
            }
322
        }
323
324
        throw new \DomainException('Could not load bootstrap file.');
325
    }
326
}
327