Completed
Pull Request — master (#127)
by
unknown
09:52
created

SymfonyExtension::loadEnv()   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 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
27
final class SymfonyExtension implements Extension
28
{
29
    /**
30
     * Kernel used inside Behat contexts or to create services injected to them.
31
     * Container is rebuilt before every scenario.
32
     */
33
    public const KERNEL_ID = 'fob_symfony.kernel';
34
35
    /**
36
     * Kernel used by Symfony driver to isolate web container from contexts' container.
37
     * Container is rebuilt before every request.
38
     */
39
    public const DRIVER_KERNEL_ID = 'fob_symfony.driver_kernel';
40
41
    /** @var bool */
42
    private $minkExtensionFound = false;
43
44
    public function getConfigKey(): string
45
    {
46
        return 'fob_symfony';
47
    }
48
49
    public function initialize(ExtensionManager $extensionManager): void
50
    {
51
        $this->registerMinkDriver($extensionManager);
52
    }
53
54
    public function configure(ArrayNodeDefinition $builder): void
55
    {
56
        $builder
57
            ->addDefaultsIfNotSet()
58
            ->children()
59
                ->scalarNode('bootstrap')->defaultNull()->end()
60
                ->arrayNode('kernel')
61
                    ->addDefaultsIfNotSet()
62
                    ->children()
63
                        ->scalarNode('path')->defaultNull()->end()
64
                        ->scalarNode('class')->defaultNull()->end()
65
                        ->scalarNode('environment')->defaultNull()->end()
66
                        ->booleanNode('debug')->defaultNull()->end()
67
                    ->end()
68
                ->end()
69
            ->end()
70
        ;
71
    }
72
73
    public function load(ContainerBuilder $container, array $config): void
74
    {
75
        $this->fallbackToTestEnvironment();
76
77
        $this->loadBootstrap($this->autodiscoverBootstrap($config['bootstrap']));
78
79
        $this->loadKernel($container, $this->autodiscoverKernelConfiguration($config['kernel']));
80
        $this->loadDriverKernel($container);
81
82
        $this->loadKernelRebooter($container);
83
84
        $this->loadEnvironmentHandler($container);
85
86
        if ($this->minkExtensionFound) {
87
            $this->loadMink($container);
88
            $this->loadMinkDefaultSession($container);
89
            $this->loadMinkParameters($container);
90
        }
91
    }
92
93
    public function process(ContainerBuilder $container): void
94
    {
95
        $this->processEnvironmentHandler($container);
96
97
        if ($this->minkExtensionFound) {
98
            $container->getDefinition(MinkExtension::MINK_ID)->setClass(Mink::class);
99
        }
100
    }
101
102
    private function registerMinkDriver(ExtensionManager $extensionManager): void
103
    {
104
        /** @var MinkExtension|null $minkExtension */
105
        $minkExtension = $extensionManager->getExtension('mink');
106
        if (null === $minkExtension) {
107
            return;
108
        }
109
110
        $minkExtension->registerDriverFactory(new SymfonyDriverFactory('symfony', new Reference(self::DRIVER_KERNEL_ID)));
111
112
        $this->minkExtensionFound = true;
113
    }
114
115
    private function loadKernel(ContainerBuilder $container, array $config): void
116
    {
117
        $definition = new Definition($config['class'], [
118
            $config['environment'] ?? $_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? 'test',
119
            (bool) ($config['debug'] ?? $_SERVER['APP_DEBUG'] ?? $_ENV['APP_DEBUG'] ?? true),
120
        ]);
121
        $definition->addMethodCall('boot');
122
        $definition->setPublic(true);
123
124
        if ($config['path'] !== null) {
125
            $definition->setFile($config['path']);
126
        }
127
128
        $container->setDefinition(self::KERNEL_ID, $definition);
129
    }
130
131
    private function loadDriverKernel(ContainerBuilder $container): void
132
    {
133
        $container->setDefinition(self::DRIVER_KERNEL_ID, $container->findDefinition(self::KERNEL_ID));
134
    }
135
136
    private function loadKernelRebooter(ContainerBuilder $container): void
137
    {
138
        $definition = new Definition(KernelOrchestrator::class, [new Reference(self::KERNEL_ID), $container]);
139
        $definition->addTag(EventDispatcherExtension::SUBSCRIBER_TAG);
140
141
        $container->setDefinition('fob_symfony.kernel_orchestrator', $definition);
142
    }
143
144
    private function loadEnvironmentHandler(ContainerBuilder $container): void
145
    {
146
        $definition = new Definition(ContextServiceEnvironmentHandler::class, [
147
            new Reference(self::KERNEL_ID),
148
            new Reference('environment.handler.context'),
149
        ]);
150
        $definition->addTag(EnvironmentExtension::HANDLER_TAG, ['priority' => 128]);
151
152
        $container->setDefinition('fob_symfony.environment_handler.context_service', $definition);
153
    }
154
155
    private function loadMink(ContainerBuilder $container): void
156
    {
157
        $container->setAlias('fob_symfony.mink', (new Alias('mink'))->setPublic(true));
158
    }
159
160
    private function loadMinkDefaultSession(ContainerBuilder $container): void
161
    {
162
        $minkDefaultSessionDefinition = new Definition(Session::class);
163
        $minkDefaultSessionDefinition->setPublic(true);
164
        $minkDefaultSessionDefinition->setFactory([new Reference('mink'), 'getSession']);
165
166
        $container->setDefinition('fob_symfony.mink.default_session', $minkDefaultSessionDefinition);
167
    }
168
169
    private function loadMinkParameters(ContainerBuilder $container): void
170
    {
171
        $minkParametersDefinition = new Definition(MinkParameters::class, [new Parameter('mink.parameters')]);
172
        $minkParametersDefinition->setPublic(true);
173
174
        $container->setDefinition('fob_symfony.mink.parameters', $minkParametersDefinition);
175
    }
176
177
    private function loadBootstrap(?string $bootstrap): void
178
    {
179
        if ($bootstrap === null) {
180
            $this->loadEnv();
181
182
            return;
183
        }
184
185
        require_once $bootstrap;
186
    }
187
188
    private function loadEnv()
189
    {
190
        $env = getenv('APP_ENV');
191
        (new Dotenv())->bootEnv(basename(dirname(__DIR__)).'/../.env', $env);
192
    }
193
194
    private function fallbackToTestEnvironment(): void
195
    {
196
        // If there's no defined server / environment variable with an environment, default to test
197
        if (($_SERVER['APP_ENV'] ?? $_ENV['APP_ENV'] ?? null) === null) {
198
            putenv('APP_ENV=' . $_SERVER['APP_ENV'] = $_ENV['APP_ENV'] = 'test');
199
        }
200
    }
201
202
    private function processEnvironmentHandler(ContainerBuilder $container): void
203
    {
204
        $definition = $container->findDefinition('fob_symfony.environment_handler.context_service');
205
        foreach ($container->findTaggedServiceIds(ContextExtension::INITIALIZER_TAG) as $serviceId => $tags) {
206
            $definition->addMethodCall('registerContextInitializer', [new Reference($serviceId)]);
207
        }
208
    }
209
210
    private function autodiscoverKernelConfiguration(array $config): array
211
    {
212
        if ($config['class'] !== null) {
213
            return $config;
214
        }
215
216
        $autodiscovered = 0;
217
218
        if (class_exists('\App\Kernel')) {
219
            $config['class'] = '\App\Kernel';
220
221
            ++$autodiscovered;
222
        }
223
224
        if (file_exists('app/AppKernel.php')) {
225
            $config['class'] = '\AppKernel';
226
            $config['path'] = 'app/AppKernel.php';
227
228
            ++$autodiscovered;
229
        }
230
231
        if ($autodiscovered !== 1) {
232
            throw new \RuntimeException(
233
                'Could not autodiscover the application kernel. ' .
234
                'Please define it manually with "FriendsOfBehat\SymfonyExtension.kernel" configuration option.'
235
            );
236
        }
237
238
        return $config;
239
    }
240
241
    /**
242
     * @param string|bool|null $bootstrap
243
     */
244
    private function autodiscoverBootstrap($bootstrap): ?string
245
    {
246
        if (is_string($bootstrap)) {
247
            return $bootstrap;
248
        }
249
250
        if ($bootstrap === false) {
251
            return null;
252
        }
253
254
        $autodiscovered = 0;
255
256
        if (file_exists('config/bootstrap.php')) {
257
            $bootstrap = 'config/bootstrap.php';
258
259
            ++$autodiscovered;
260
        }
261
262
        if (file_exists('app/autoload.php')) {
263
            $bootstrap = 'app/autoload.php';
264
265
            ++$autodiscovered;
266
        }
267
268
        if ($autodiscovered === 2) {
269
            throw new \RuntimeException(
270
                'Could not autodiscover the bootstrap file. ' .
271
                'Please define it manually with "FriendsOfBehat\SymfonyExtension.bootstrap" configuration option. ' .
272
                'Setting that option to "false" disables autodiscovering.'
273
            );
274
        }
275
276
        return is_string($bootstrap) ? $bootstrap : null;
277
    }
278
}
279