Completed
Pull Request — master (#127)
by
unknown
01:18
created

SymfonyExtension::loadEnv()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 2
nc 2
nop 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace FriendsOfBehat\SymfonyExtension\ServiceContainer;
6
7
use Behat\Behat\Context\ServiceContainer\ContextExtension;
8
use Behat\Mink\Session;
9
use Behat\MinkExtension\ServiceContainer\MinkExtension;
10
use Behat\Testwork\Environment\ServiceContainer\EnvironmentExtension;
11
use Behat\Testwork\EventDispatcher\ServiceContainer\EventDispatcherExtension;
12
use Behat\Testwork\ServiceContainer\Extension;
13
use Behat\Testwork\ServiceContainer\ExtensionManager;
14
use FriendsOfBehat\SymfonyExtension\Context\Environment\Handler\ContextServiceEnvironmentHandler;
15
use FriendsOfBehat\SymfonyExtension\Driver\Factory\SymfonyDriverFactory;
16
use FriendsOfBehat\SymfonyExtension\Listener\KernelOrchestrator;
17
use FriendsOfBehat\SymfonyExtension\Mink\Mink;
18
use FriendsOfBehat\SymfonyExtension\Mink\MinkParameters;
19
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
20
use Symfony\Component\DependencyInjection\Alias;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Definition;
23
use Symfony\Component\DependencyInjection\Parameter;
24
use Symfony\Component\DependencyInjection\Reference;
25
use Symfony\Component\Dotenv\Dotenv;
26
use Symfony\Component\Dotenv\Exception\PathException;
27
28
final class SymfonyExtension implements Extension
29
{
30
    /**
31
     * Kernel used inside Behat contexts or to create services injected to them.
32
     * Container is rebuilt before every scenario.
33
     */
34
    public const KERNEL_ID = 'fob_symfony.kernel';
35
36
    /**
37
     * Kernel used by Symfony driver to isolate web container from contexts' container.
38
     * Container is rebuilt before every request.
39
     */
40
    public const DRIVER_KERNEL_ID = 'fob_symfony.driver_kernel';
41
42
    /** @var bool */
43
    private $minkExtensionFound = false;
44
45
    public function getConfigKey(): string
46
    {
47
        return 'fob_symfony';
48
    }
49
50
    public function initialize(ExtensionManager $extensionManager): void
51
    {
52
        $this->registerMinkDriver($extensionManager);
53
    }
54
55
    public function configure(ArrayNodeDefinition $builder): void
56
    {
57
        $builder
58
            ->addDefaultsIfNotSet()
59
            ->children()
60
                ->scalarNode('bootstrap')->defaultNull()->end()
61
                ->arrayNode('kernel')
62
                    ->addDefaultsIfNotSet()
63
                    ->children()
64
                        ->scalarNode('path')->defaultNull()->end()
65
                        ->scalarNode('class')->defaultNull()->end()
66
                        ->scalarNode('environment')->defaultNull()->end()
67
                        ->booleanNode('debug')->defaultNull()->end()
68
                    ->end()
69
                ->end()
70
            ->end()
71
        ;
72
    }
73
74
    public function load(ContainerBuilder $container, array $config): void
75
    {
76
        $this->fallbackToTestEnvironment();
77
78
        $this->loadBootstrap($this->autodiscoverBootstrap($config['bootstrap']));
79
80
        $this->loadKernel($container, $this->autodiscoverKernelConfiguration($config['kernel']));
81
        $this->loadDriverKernel($container);
82
83
        $this->loadKernelRebooter($container);
84
85
        $this->loadEnvironmentHandler($container);
86
87
        if ($this->minkExtensionFound) {
88
            $this->loadMink($container);
89
            $this->loadMinkDefaultSession($container);
90
            $this->loadMinkParameters($container);
91
        }
92
    }
93
94
    public function process(ContainerBuilder $container): void
95
    {
96
        $this->processEnvironmentHandler($container);
97
98
        if ($this->minkExtensionFound) {
99
            $container->getDefinition(MinkExtension::MINK_ID)->setClass(Mink::class);
100
        }
101
    }
102
103
    private function registerMinkDriver(ExtensionManager $extensionManager): void
104
    {
105
        /** @var MinkExtension|null $minkExtension */
106
        $minkExtension = $extensionManager->getExtension('mink');
107
        if (null === $minkExtension) {
108
            return;
109
        }
110
111
        $minkExtension->registerDriverFactory(new SymfonyDriverFactory('symfony', new Reference(self::DRIVER_KERNEL_ID)));
112
113
        $this->minkExtensionFound = true;
114
    }
115
116
    private function loadKernel(ContainerBuilder $container, array $config): void
117
    {
118
        $definition = new Definition($config['class'], [
119
            $config['environment'] ?? $_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? 'test',
120
            (bool) ($config['debug'] ?? $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? true),
121
        ]);
122
        $definition->addMethodCall('boot');
123
        $definition->setPublic(true);
124
125
        if ($config['path'] !== null) {
126
            $definition->setFile($config['path']);
127
        }
128
129
        $container->setDefinition(self::KERNEL_ID, $definition);
130
    }
131
132
    private function loadDriverKernel(ContainerBuilder $container): void
133
    {
134
        $container->setDefinition(self::DRIVER_KERNEL_ID, $container->findDefinition(self::KERNEL_ID));
135
    }
136
137
    private function loadKernelRebooter(ContainerBuilder $container): void
138
    {
139
        $definition = new Definition(KernelOrchestrator::class, [new Reference(self::KERNEL_ID), $container]);
140
        $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG);
141
142
        $container->setDefinition('fob_symfony.kernel_orchestrator', $definition);
143
    }
144
145
    private function loadEnvironmentHandler(ContainerBuilder $container): void
146
    {
147
        $definition = new Definition(ContextServiceEnvironmentHandler::class, [
148
            new Reference(self::KERNEL_ID),
149
            new Reference('environment.handler.context'),
150
        ]);
151
        $definition->addTag(EnvironmentExtension::HANDLER_TAG, ['priority' => 128]);
152
153
        $container->setDefinition('fob_symfony.environment_handler.context_service', $definition);
154
    }
155
156
    private function loadMink(ContainerBuilder $container): void
157
    {
158
        $container->setAlias('fob_symfony.mink', (new Alias('mink'))->setPublic(true));
159
    }
160
161
    private function loadMinkDefaultSession(ContainerBuilder $container): void
162
    {
163
        $minkDefaultSessionDefinition = new Definition(Session::class);
164
        $minkDefaultSessionDefinition->setPublic(true);
165
        $minkDefaultSessionDefinition->setFactory([new Reference('mink'), 'getSession']);
166
167
        $container->setDefinition('fob_symfony.mink.default_session', $minkDefaultSessionDefinition);
168
    }
169
170
    private function loadMinkParameters(ContainerBuilder $container): void
171
    {
172
        $minkParametersDefinition = new Definition(MinkParameters::class, [new Parameter('mink.parameters')]);
173
        $minkParametersDefinition->setPublic(true);
174
175
        $container->setDefinition('fob_symfony.mink.parameters', $minkParametersDefinition);
176
    }
177
178
    private function loadBootstrap(?string $bootstrap): void
179
    {
180
        if ($bootstrap === null) {
181
            $this->loadEnv();
182
183
            return;
184
        }
185
186
        require_once $bootstrap;
187
    }
188
189
    private function loadEnv()
190
    {
191
        $env = $config['environment'] ?? $_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? 'test';
192
        try {
193
            (new Dotenv())->bootEnv(basename(dirname(__DIR__)).'/../.env', $env);
194
        }
195
        catch (PathException $exception) {
0 ignored issues
show
Bug introduced by
The class Symfony\Component\Dotenv\Exception\PathException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
196
            return null;
197
        }
198
    }
199
200
    private function fallbackToTestEnvironment(): void
201
    {
202
        // If there's no defined server / environment variable with an environment, default to test
203
        if (($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) === null) {
204
            putenv('APP_ENV=' . $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = 'test');
205
        }
206
    }
207
208
    private function processEnvironmentHandler(ContainerBuilder $container): void
209
    {
210
        $definition = $container->findDefinition('fob_symfony.environment_handler.context_service');
211
        foreach ($container->findTaggedServiceIds(ContextExtension::INITIALIZER_TAG) as $serviceId => $tags) {
212
            $definition->addMethodCall('registerContextInitializer', [new Reference($serviceId)]);
213
        }
214
    }
215
216
    private function autodiscoverKernelConfiguration(array $config): array
217
    {
218
        if ($config['class'] !== null) {
219
            return $config;
220
        }
221
222
        $autodiscovered = 0;
223
224
        if (class_exists('\App\Kernel')) {
225
            $config['class'] = '\App\Kernel';
226
227
            ++$autodiscovered;
228
        }
229
230
        if (file_exists('app/AppKernel.php')) {
231
            $config['class'] = '\AppKernel';
232
            $config['path'] = 'app/AppKernel.php';
233
234
            ++$autodiscovered;
235
        }
236
237
        if ($autodiscovered !== 1) {
238
            throw new \RuntimeException(
239
                'Could not autodiscover the application kernel. ' .
240
                'Please define it manually with "FriendsOfBehat\SymfonyExtension.kernel" configuration option.'
241
            );
242
        }
243
244
        return $config;
245
    }
246
247
    /**
248
     * @param string|bool|null $bootstrap
249
     */
250
    private function autodiscoverBootstrap($bootstrap): ?string
251
    {
252
        if (is_string($bootstrap)) {
253
            return $bootstrap;
254
        }
255
256
        if ($bootstrap === false) {
257
            return null;
258
        }
259
260
        $autodiscovered = 0;
261
262
        if (file_exists('config/bootstrap.php')) {
263
            $bootstrap = 'config/bootstrap.php';
264
265
            ++$autodiscovered;
266
        }
267
268
        if (file_exists('app/autoload.php')) {
269
            $bootstrap = 'app/autoload.php';
270
271
            ++$autodiscovered;
272
        }
273
274
        if ($autodiscovered === 2) {
275
            throw new \RuntimeException(
276
                'Could not autodiscover the bootstrap file. ' .
277
                'Please define it manually with "FriendsOfBehat\SymfonyExtension.bootstrap" configuration option. ' .
278
                'Setting that option to "false" disables autodiscovering.'
279
            );
280
        }
281
282
        return is_string($bootstrap) ? $bootstrap : null;
283
    }
284
}
285