Completed
Pull Request — master (#69)
by Kamil
02:16
created

SymfonyExtension::loadMink()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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