Completed
Push — master ( 12d4c5...4dd58a )
by Łukasz
12s queued 10s
created

SymfonyExtension::loadEnvVars()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FriendsOfBehat\SymfonyExtension\ServiceContainer;
6
7
use Behat\MinkExtension\ServiceContainer\MinkExtension;
8
use Behat\Testwork\EventDispatcher\ServiceContainer\EventDispatcherExtension;
9
use Behat\Testwork\ServiceContainer\Extension;
10
use Behat\Testwork\ServiceContainer\ExtensionManager;
11
use FriendsOfBehat\CrossContainerExtension\CrossContainerProcessor;
12
use FriendsOfBehat\CrossContainerExtension\KernelBasedContainerAccessor;
13
use FriendsOfBehat\CrossContainerExtension\ServiceContainer\CrossContainerExtension;
14
use FriendsOfBehat\SymfonyExtension\Driver\Factory\SymfonyDriverFactory;
15
use FriendsOfBehat\SymfonyExtension\Listener\KernelRebooter;
16
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
17
use Symfony\Component\DependencyInjection\Container;
18
use Symfony\Component\DependencyInjection\ContainerBuilder;
19
use Symfony\Component\DependencyInjection\Definition;
20
use Symfony\Component\DependencyInjection\Reference;
21
use Symfony\Component\Dotenv\Dotenv;
22
use Symfony\Component\HttpKernel\KernelInterface;
23
24
final class SymfonyExtension implements Extension
25
{
26
    /**
27
     * Kernel used inside Behat contexts or to create services injected to them.
28
     * Container is built before every scenario.
29
     */
30
    const KERNEL_ID = 'sylius_symfony_extension.kernel';
31
32
    /**
33
     * The current container used in scenario contexts.
34
     * To be used as a factory for current injected application services.
35
     */
36
    const KERNEL_CONTAINER_ID = 'sylius_symfony_extension.kernel.container';
37
38
    /**
39
     * Kernel used by Symfony2 driver to isolate web container from contexts' container.
40
     * Container is built before every request.
41
     */
42
    const DRIVER_KERNEL_ID = 'sylius_symfony_extension.driver_kernel';
43
44
    /**
45
     * Kernel that should be used by extensions only.
46
     * Container is built only once at the first use.
47
     */
48
    const SHARED_KERNEL_ID = 'sylius_symfony_extension.shared_kernel';
49
50
    /**
51
     * The only container built by shared kernel.
52
     * To be used as a factory for shared injected application services.
53
     */
54
    const SHARED_KERNEL_CONTAINER_ID = 'sylius_symfony_extension.shared_kernel.container';
55
56
    /**
57
     * Default symfony environment used to run your suites.
58
     */
59
    private const DEFAULT_ENV = 'test';
60
61
    /**
62
     * Enable or disable the debug mode
63
     */
64
    private const DEBUG_MODE = false;
65
66
    /**
67
     * @var CrossContainerProcessor|null
68
     */
69
    private $crossContainerProcessor;
70
71
    /**
72
     * {@inheritdoc}
73
     */
74
    public function getConfigKey(): string
75
    {
76
        return 'fob_symfony';
77
    }
78
79
    /**
80
     * {@inheritdoc}
81
     */
82
    public function initialize(ExtensionManager $extensionManager): void
83
    {
84
        $this->registerSymfonyDriverFactory($extensionManager);
85
        $this->initializeCrossContainerProcessor($extensionManager);
86
    }
87
88
    /**
89
     * {@inheritdoc}
90
     */
91
    public function configure(ArrayNodeDefinition $builder): void
92
    {
93
        $builder
94
            ->addDefaultsIfNotSet()
95
                ->children()
96
                    ->scalarNode('env_file')->defaultNull()->end()
97
                    ->arrayNode('kernel')
98
                        ->addDefaultsIfNotSet()
99
                        ->children()
100
                            ->scalarNode('bootstrap')->defaultValue('app/autoload.php')->end()
101
                            ->scalarNode('path')->defaultValue('app/AppKernel.php')->end()
102
                            ->scalarNode('class')->defaultValue('AppKernel')->end()
103
                            ->scalarNode('env')->defaultValue('test')->end()
104
                            ->booleanNode('debug')->defaultTrue()->end()
105
                        ->end()
106
                    ->end()
107
                ->end()
108
            ->end()
109
        ;
110
    }
111
112
    /**
113
     * {@inheritdoc}
114
     */
115
    public function load(ContainerBuilder $container, array $config): void
116
    {
117
        if (null !== $config['env_file']) {
118
            $envFilePath = sprintf('%s/%s', $container->getParameter('paths.base'), $config['env_file']);
119
            $envFilePath = file_exists($envFilePath) ? $envFilePath : $envFilePath.'.dist';
120
            (new Dotenv())->load($envFilePath);
121
122
            $environment = false !== getenv('APP_ENV') ? getenv('APP_ENV') : self::DEFAULT_ENV;
123
            $debugMode = false !== getenv('APP_DEBUG') ? getenv('APP_DEBUG') : self::DEBUG_MODE;
124
125
            $config['kernel']['env'] = $environment;
126
            $config['kernel']['kernel'] = $debugMode;
127
        }
128
129
        $this->loadKernel($container, $config['kernel']);
130
        $this->loadKernelContainer($container);
131
132
        $this->loadDriverKernel($container);
133
134
        $this->loadSharedKernel($container);
135
        $this->loadSharedKernelContainer($container);
136
137
        $this->loadKernelRebooter($container);
138
        $this->declareSymfonyContainers($container);
139
    }
140
141
    /**
142
     * {@inheritdoc}
143
     */
144
    public function process(ContainerBuilder $container): void
145
    {
146
    }
147
148
    /**
149
     * @param ContainerBuilder $container
150
     */
151
    private function loadKernel(ContainerBuilder $container, array $config): void
152
    {
153
        $definition = new Definition($config['class'], [
154
            $config['env'],
155
            $config['debug'],
156
        ]);
157
        $definition->addMethodCall('boot');
158
        $definition->setPublic(true);
159
160
        $file = $this->getKernelFile($container->getParameter('paths.base'), $config['path']);
161
        if (null !== $file) {
162
            $definition->setFile($file);
163
        }
164
165
        $container->setDefinition(self::KERNEL_ID, $definition);
166
167
        $this->requireKernelBootstrapFile($container->getParameter('paths.base'), $config['bootstrap']);
168
    }
169
170
    /**
171
     * @param ContainerBuilder $container
172
     */
173
    private function loadKernelContainer(ContainerBuilder $container): void
174
    {
175
        $containerDefinition = new Definition(Container::class);
176
        $containerDefinition->setFactory([
177
            new Reference(self::KERNEL_ID),
178
            'getContainer',
179
        ]);
180
181
        $container->setDefinition(self::KERNEL_CONTAINER_ID, $containerDefinition);
182
    }
183
184
    /**
185
     * @param ContainerBuilder $container
186
     */
187
    private function loadDriverKernel(ContainerBuilder $container): void
188
    {
189
        $container->setDefinition(self::DRIVER_KERNEL_ID, $container->findDefinition(self::KERNEL_ID));
190
    }
191
192
    /**
193
     * @param ContainerBuilder $container
194
     */
195
    private function loadSharedKernel(ContainerBuilder $container): void
196
    {
197
        $container->setDefinition(self::SHARED_KERNEL_ID, $container->findDefinition(self::KERNEL_ID));
198
    }
199
200
    /**
201
     * @param ContainerBuilder $container
202
     */
203
    private function loadSharedKernelContainer(ContainerBuilder $container): void
204
    {
205
        $containerDefinition = new Definition(Container::class);
206
        $containerDefinition->setFactory([
207
            new Reference(self::SHARED_KERNEL_ID),
208
            'getContainer',
209
        ]);
210
211
        $container->setDefinition(self::SHARED_KERNEL_CONTAINER_ID, $containerDefinition);
212
    }
213
214
    /**
215
     * @param ContainerBuilder $container
216
     *
217
     * @throws \Exception
218
     */
219
    private function loadKernelRebooter(ContainerBuilder $container): void
220
    {
221
        $definition = new Definition(KernelRebooter::class, [new Reference(self::KERNEL_ID)]);
222
        $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG);
223
224
        $container->setDefinition(self::KERNEL_ID . '.rebooter', $definition);
225
    }
226
227
    /**
228
     * @param ContainerBuilder $container
229
     *
230
     * @throws \Exception
231
     */
232
    private function declareSymfonyContainers(ContainerBuilder $container): void
233
    {
234
        if (null === $this->crossContainerProcessor) {
235
            return;
236
        }
237
238
        $containerAccessors = [
239
            'symfony' => self::KERNEL_ID,
240
            'symfony_driver' => self::DRIVER_KERNEL_ID,
241
            'symfony_shared' => self::SHARED_KERNEL_ID,
242
        ];
243
244
        foreach ($containerAccessors as $containerName => $kernelIdentifier) {
245
            $kernel = $container->get($kernelIdentifier);
246
247
            if (!$kernel instanceof KernelInterface) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\HttpKernel\KernelInterface does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
248
                throw new \RuntimeException(sprintf(
249
                    'Expected service "%s" to be an instance of "%s", got "%s" instead.',
250
                    $kernelIdentifier,
251
                    KernelInterface::class,
252
                    \is_object($kernel) ? \get_class($kernel) : \gettype($kernel)
253
                ));
254
            }
255
256
            $this->crossContainerProcessor->addContainerAccessor($containerName, new KernelBasedContainerAccessor($kernel));
257
        }
258
    }
259
260
    /**
261
     * @param ExtensionManager $extensionManager
262
     */
263
    private function initializeCrossContainerProcessor(ExtensionManager $extensionManager): void
264
    {
265
        /** @var CrossContainerExtension $extension */
266
        $extension = $extensionManager->getExtension('fob_cross_container');
267
        if (null !== $extension) {
268
            $this->crossContainerProcessor = $extension->getCrossContainerProcessor();
269
        }
270
    }
271
272
    /**
273
     * @param ExtensionManager $extensionManager
274
     */
275
    private function registerSymfonyDriverFactory(ExtensionManager $extensionManager): void
276
    {
277
        /** @var MinkExtension|null $minkExtension */
278
        $minkExtension = $extensionManager->getExtension('mink');
279
        if (null === $minkExtension) {
280
            return;
281
        }
282
283
        $minkExtension->registerDriverFactory(new SymfonyDriverFactory(
284
            'symfony',
285
            new Reference(self::DRIVER_KERNEL_ID)
286
        ));
287
    }
288
289
    /**
290
     * @param string $basePath
291
     * @param string $kernelPath
292
     *
293
     * @return string|null
294
     */
295
    private function getKernelFile(string $basePath, string $kernelPath): ?string
296
    {
297
        $possibleFiles = [
298
            sprintf('%s/%s', $basePath, $kernelPath),
299
            $kernelPath,
300
        ];
301
302
        foreach ($possibleFiles as $possibleFile) {
303
            if (file_exists($possibleFile)) {
304
                return $possibleFile;
305
            }
306
        }
307
308
        return null;
309
    }
310
311
    /**
312
     * @param string $basePath
313
     * @param string|null $bootstrapPath
314
     *
315
     * @throws \DomainException
316
     */
317
    private function requireKernelBootstrapFile(string $basePath, ?string $bootstrapPath): void
318
    {
319
        if (null === $bootstrapPath) {
320
            return;
321
        }
322
323
        $possiblePaths = [
324
            sprintf('%s/%s', $basePath, $bootstrapPath),
325
            $bootstrapPath,
326
        ];
327
328
        foreach ($possiblePaths as $possiblePath) {
329
            if (file_exists($possiblePath)) {
330
                require_once $possiblePath;
331
332
                return;
333
            }
334
        }
335
336
        throw new \DomainException('Could not load bootstrap file.');
337
    }
338
}
339