Test Failed
Pull Request — master (#4)
by Vincent
03:07
created

PrimeExtension::configureMongo()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 11
c 0
b 0
f 0
nc 3
nop 3
dl 0
loc 17
rs 9.9
ccs 7
cts 7
cp 1
crap 3
1
<?php
2
3
namespace Bdf\PrimeBundle\DependencyInjection;
4
5
use Bdf\Prime\Cache\DoctrineCacheAdapter;
6
use Bdf\Prime\Configuration as PrimeConfiguration;
7
use Bdf\Prime\Connection\ConnectionRegistry;
8
use Bdf\Prime\Connection\Factory\ConnectionFactory;
9
use Bdf\Prime\Connection\Factory\MasterSlaveConnectionFactory;
10
use Bdf\Prime\Connection\Factory\ShardingConnectionFactory;
11
use Bdf\Prime\Mapper\MapperFactory;
12
use Bdf\Prime\MongoDB\Collection\MongoCollectionLocator;
0 ignored issues
show
Bug introduced by
The type Bdf\Prime\MongoDB\Collec...\MongoCollectionLocator was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
13
use Bdf\Prime\MongoDB\Document\DocumentMapperInterface;
0 ignored issues
show
Bug introduced by
The type Bdf\Prime\MongoDB\Document\DocumentMapperInterface was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
14
use Bdf\Prime\MongoDB\Document\Hydrator\DocumentHydratorFactory;
0 ignored issues
show
Bug introduced by
The type Bdf\Prime\MongoDB\Docume...DocumentHydratorFactory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
15
use Bdf\Prime\Types\TypesRegistryInterface;
16
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
17
use Symfony\Component\Cache\Psr16Cache;
18
use Symfony\Component\Config\FileLocator;
19
use Symfony\Component\Config\Loader\FileLoader;
20
use Symfony\Component\DependencyInjection\ChildDefinition;
21
use Symfony\Component\DependencyInjection\ContainerBuilder;
22
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
23
use Symfony\Component\DependencyInjection\ExpressionLanguage;
24
use Symfony\Component\DependencyInjection\Extension\Extension;
25
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
26
use Symfony\Component\DependencyInjection\Reference;
27
28
/**
29
 * PrimeExtension
30
 */
31 6
class PrimeExtension extends Extension
32
{
33 6
    /**
34 6
     * {@inheritDoc}
35
     */
36 6
    public function load(array $configs, ContainerBuilder $container)
37 6
    {
38 6
        $configuration = $this->getConfiguration($configs, $container);
39
        $config = $this->processConfiguration($configuration, $configs);
40 6
41 6
        $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
42 6
        $loader->load('prime.yaml');
43
        $loader->load('collector.yaml');
44 6
45 6
        $this->configureConnection($config, $container);
46 6
        $this->configureMapperCache($config, $container);
47 6
        $this->configureSerializer($config, $container);
48 6
49 6
        if (class_exists(MongoCollectionLocator::class)) {
50
            $this->configureMongo($loader, $container, $config);
51
        }
52
53
        $container->setParameter('prime.default_connection', $config['default_connection']);
54
        $container->setParameter('prime.migration.connection', $config['migration']['connection']);
55
        $container->setParameter('prime.migration.table', $config['migration']['table']);
56 6
        $container->setParameter('prime.migration.path', $config['migration']['path']);
57
        $container->setParameter('prime.hydrators', $config['hydrators']);
58 6
        $container->setParameter('prime.locatorizable', $config['activerecord']);
59 6
    }
60
61
    /**
62 6
     * @param array $config
63
     * @param ContainerBuilder $container
64 6
     */
65 6
    public function configureConnection(array $config, ContainerBuilder $container)
66 6
    {
67 6
        foreach ($config['connections'] as $name => &$options) {
68
            $options = $this->cleanConnectionOptions($options);
69
70 6
            // Overwrite global config by the connection config parameters and create the configuration reference.
71 6
            $this->createConfiguration($name, $this->mergeConfiguration($config, $options), $container);
72 6
73 6
            if (!$container->hasDefinition(MasterSlaveConnectionFactory::class) && $this->hasConnectionOption('read', $options)) {
74
                $container->register(MasterSlaveConnectionFactory::class, MasterSlaveConnectionFactory::class)
75
                    ->addTag('bdf_prime.connection_factory', ['priority' => -255])
76
                    ->addArgument(new Reference(ConnectionFactory::class));
77 6
            }
78 6
79
            if (!$container->hasDefinition(ShardingConnectionFactory::class) && $this->hasConnectionOption('shards', $options)) {
80
                $container->register(ShardingConnectionFactory::class, ShardingConnectionFactory::class)
81
                    ->addTag('bdf_prime.connection_factory', ['priority' => -256])
82
                    ->addArgument(new Reference(ConnectionFactory::class));
83
            }
84
        }
85
86 6
        $registry = $container->getDefinition(ConnectionRegistry::class);
87
        $registry->replaceArgument(0, $config['connections']);
88 6
    }
89 1
90
    /**
91
     * @param string $option
92 6
     * @param array $options
93
     * @return bool
94
     */
95
    private function hasConnectionOption(string $option, array $options): bool
96
    {
97 6
        if (isset($options[$option])) {
98
            return true;
99
        }
100
101
        if (!isset($options['url'])) {
102
            return false;
103
        }
104 6
105
        // The option could be in the url. Adding the factory by default.
106 6
        return true;
107 6
    }
108
109
    /**
110
     * @param array $config
111
     * @param ContainerBuilder $container
112
     */
113
    public function configureSerializer(array $config, ContainerBuilder $container)
114
    {
115
        if (!isset($config['serializer'])) {
116
            return;
117
        }
118 6
119
        $prime = $container->findDefinition('prime');
120 6
        $prime->addMethodCall('setSerializer', [new Reference($config['serializer'])]);
121
    }
122 6
123 2
    /**
124
     * @param array $config
125 2
     * @param ContainerBuilder $container
126 2
     */
127
    public function configureMapperCache(array $config, ContainerBuilder $container)
128
    {
129
        $definition = $container->getDefinition(MapperFactory::class);
130 6
131
        if (isset($config['cache']['query'])) {
132
            $ref = $this->createResultCacheReference('prime.cache.query', $config['cache']['query'], $container);
133
134
            if ($ref !== null) {
135
                $definition->replaceArgument(2, $ref);
136
            }
137
        }
138
139
        if (isset($config['cache']['metadata'])) {
140
            $ref = $this->createCacheReference('prime.cache.metadata', $config['cache']['metadata'], $container);
141
142
            if ($ref !== null) {
143
                $definition->replaceArgument(1, $ref);
144
            }
145 6
        }
146
    }
147
148 6
    /**
149 6
     * @param FileLoader $loader
150 6
     * @param ContainerBuilder $container
151 6
     * @param array $config
152
     * @return void
153
     * @throws \Exception
154
     */
155
    public function configureMongo(FileLoader $loader, ContainerBuilder $container, array $config): void
156
    {
157
        $loader->load('prime_mongo.yaml');
158
159
        $container->registerForAutoconfiguration(DocumentMapperInterface::class)
160
            ->setPublic(true)
161
            ->setShared(false)
162 6
            ->setAutowired(true)
163
            ->addArgument(new ExpressionLanguage())
164 6
        ;
165
166 6
        if (isset($config['cache']['metadata'])) {
167 6
            $definition = $container->findDefinition(DocumentHydratorFactory::class);
168 6
            $ref = $this->createCacheReference('prime.cache.metadata', $config['cache']['metadata'], $container);
169
170 6
            if ($ref !== null) {
171 6
                $definition->replaceArgument(0, $ref);
172 2
            }
173
        }
174
    }
175 6
176 2
    /**
177
     * @param array $globalConfig
178
     * @param array $config
179 6
     *
180 6
     * @return array
181 6
     */
182
    public function mergeConfiguration(array $globalConfig, array $config): array
183
    {
184 6
        return [
185 6
            'types' => array_merge($globalConfig['types'], $config['types']),
186
            'auto_commit' => $config['auto_commit'] ?? $globalConfig['auto_commit'],
187 6
            'logging' => $config['logging'] ?? $globalConfig['logging'],
188 6
            'profiling' => $config['profiling'] ?? $globalConfig['profiling'],
189 6
        ];
190
    }
191 6
192
    /**
193
     * Create and declare the configuration definition of the connection
194
     *
195
     * @param string $name
196
     * @param array $config
197 6
     * @param ContainerBuilder $container
198 6
     */
199
    public function createConfiguration(string $name, array $config, ContainerBuilder $container): void
200
    {
201
        $namespace = "prime.{$name}_connection";
202
203
        $configuration = $container->setDefinition("$namespace.configuration", new ChildDefinition(PrimeConfiguration::class));
204
        $configuration->setPublic(true);
205
        $configuration->addMethodCall('setTypes', [new Reference("$namespace.types")]);
206
207
        $typeRegistry = $container->setDefinition("$namespace.types", new ChildDefinition(TypesRegistryInterface::class));
208
        foreach ($config['types'] as $type => $info) {
209
            $typeRegistry->addMethodCall('register', [$info['class'], is_int($type) ? null : $type]);
210
        }
211
212
        if (isset($config['auto_commit'])) {
213
            $configuration->addMethodCall('setAutoCommit', [$config['auto_commit']]);
214
        }
215
216
        $logger = null;
217
        if ($config['logging']) {
218
            $logger = new Reference('prime.logger');
219
        }
220
221
        if ($config['profiling']) {
222
            $profilingLogger = new Reference('prime.logger.profiling');
223
224
            if ($logger !== null) {
225
                $chainLogger = $container->findDefinition('prime.logger.chain');
226
                $chainLogger->replaceArgument(0, [$logger, $profilingLogger]);
227
228
                $logger = new Reference('prime.logger.chain');
229
            } else {
230
                $logger = $profilingLogger;
231
            }
232
        }
233
234 2
        if ($logger) {
235
            $configuration->addMethodCall('setSQLLogger', [$logger]);
236 2
        }
237 1
    }
238
239
    /**
240 1
     * @param array $config
241 1
     * @param ContainerBuilder $container
242 1
     *
243 1
     * @return null|Reference
244 1
     */
245
    private function createCacheReference(string $namespace, array $config, ContainerBuilder $container)
246 1
    {
247 1
        if (isset($config['service'])) {
248
            return new Reference($config['service']);
249
        }
250 1
251
        if (isset($config['pool'])) {
252
            if (!$container->has($namespace)) {
253
                $definition = $container->register($namespace, Psr16Cache::class);
254
                $definition->addArgument(new Reference($config['pool']));
255
            }
256
257
            return new Reference($namespace);
258
        }
259
260
        return null;
261
    }
262
263 6
    /**
264
     * Create the cache result service
265 6
     *
266
     * @param array $config
267
     * @param ContainerBuilder $container
268
     *
269
     * @return null|Reference
270
     */
271
    private function createResultCacheReference(string $namespace, array $config, ContainerBuilder $container)
272 6
    {
273
        if (isset($config['service'])) {
274
            return new Reference($config['service']);
275
        }
276
277
        if (isset($config['pool'])) {
278
            if (!$container->has($namespace)) {
279
                $definition = $container->register($namespace.'.doctrine-provider', DoctrineProvider::class);
280
                $definition->setFactory([DoctrineProvider::class, 'wrap']);
281
                $definition->addArgument(new Reference($config['pool']));
282
283
                $definition = $container->register($namespace, DoctrineCacheAdapter::class);
284
                $definition->addArgument(new Reference($namespace.'.doctrine-provider'));
285
            }
286 6
287 6
            return new Reference($namespace);
288 6
        }
289 6
290
        return null;
291
    }
292
293 6
    /**
294
     * Rearrange the key name of the configuration
295
     *
296
     * @param array $options
297
     *
298 6
     * @return array
299
     */
300 6
    private function cleanConnectionOptions(array $options): array
301 6
    {
302 6
        if (isset($options['platform_service'])) {
303
            $options['platform'] = new Reference($options['platform_service']);
304
            unset($options['platform_service']);
305
        }
306 6
307
//        unset($options['mapping_types']);
308
309
        if (isset($options['shard_choser'])) {
310
            $options['shard_choser'] = new Reference($options['shard_choser']);
311
        }
312 6
313
        $parameters = [
314 6
            'options' => 'driverOptions',
315
            'driver_class' => 'driverClass',
316
            'wrapper_class' => 'wrapperClass',
317
            'shard_choser' => 'shardChoser',
318
            'distribution_key' => 'distributionKey',
319
            'server_version' => 'serverVersion',
320
            'default_table_options' => 'defaultTableOptions',
321
        ];
322
323
        foreach ($parameters as $old => $new) {
324
            if (isset($options[$old])) {
325
                $options[$new] = $options[$old];
326
                unset($options[$old]);
327
            }
328
        }
329
330
        if (!empty($options['read']) && !empty($options['shards'])) {
331
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
332
        }
333
334
335
        $parameters = ['read', 'shards', 'driverOptions', 'defaultTableOptions'];
336
337
        foreach ($parameters as $key) {
338
            if (empty($options[$key])) {
339
                unset($options[$key]);
340
            }
341
        }
342
343
        return $options;
344
    }
345
346
    /**
347
     * {@inheritDoc}
348
     */
349
    public function getConfiguration(array $config, ContainerBuilder $container)
350
    {
351
        return new Configuration($container->getParameter('kernel.debug'));
352
    }
353
}
354