Passed
Pull Request — master (#4)
by Vincent
02:59
created

PrimeExtension::load()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 3.0013

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 18
nc 4
nop 2
dl 0
loc 27
ccs 18
cts 19
cp 0.9474
crap 3.0013
rs 9.6666
c 2
b 0
f 0
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\Schema\RepositoryUpgraderResolver;
16
use Bdf\Prime\Schema\StructureUpgraderResolverAggregate;
17
use Bdf\Prime\Schema\StructureUpgraderResolverInterface;
18
use Bdf\Prime\ServiceLocator;
19
use Bdf\Prime\Types\TypesRegistryInterface;
20
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
21
use Symfony\Component\Cache\Psr16Cache;
22
use Symfony\Component\Config\FileLocator;
23
use Symfony\Component\Config\Loader\FileLoader;
24
use Symfony\Component\DependencyInjection\ChildDefinition;
25
use Symfony\Component\DependencyInjection\ContainerBuilder;
26
use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
27
use Symfony\Component\DependencyInjection\Extension\Extension;
28
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
29
use Symfony\Component\DependencyInjection\Reference;
30
31
/**
32
 * PrimeExtension
33
 */
34
class PrimeExtension extends Extension
35
{
36
    /**
37
     * {@inheritDoc}
38
     */
39 6
    public function load(array $configs, ContainerBuilder $container)
40
    {
41 6
        $configuration = $this->getConfiguration($configs, $container);
42 6
        $config = $this->processConfiguration($configuration, $configs);
43
44 6
        $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config'));
45 6
        $loader->load('prime.yaml');
46 6
        $loader->load('collector.yaml');
47
48 6
        $this->configureConnection($config, $container);
49 6
        $this->configureMapperCache($config, $container);
50 6
        $this->configureSerializer($config, $container);
51
52 6
        if (class_exists(MongoCollectionLocator::class)) {
53
            $this->configureMongo($loader, $container, $config);
54
        }
55
56 6
        if (interface_exists(StructureUpgraderResolverInterface::class)) {
57 6
            $this->configureUpgrader($config, $container);
58
        }
59
60 6
        $container->setParameter('prime.default_connection', $config['default_connection']);
61 6
        $container->setParameter('prime.migration.connection', $config['migration']['connection']);
62 6
        $container->setParameter('prime.migration.table', $config['migration']['table']);
63 6
        $container->setParameter('prime.migration.path', $config['migration']['path']);
64 6
        $container->setParameter('prime.hydrators', $config['hydrators']);
65 6
        $container->setParameter('prime.locatorizable', $config['activerecord']);
66
    }
67
68
    /**
69
     * @param array $config
70
     * @param ContainerBuilder $container
71
     */
72 6
    public function configureConnection(array $config, ContainerBuilder $container)
73
    {
74 6
        foreach ($config['connections'] as $name => &$options) {
75 6
            $options = $this->cleanConnectionOptions($options);
76
77
            // Overwrite global config by the connection config parameters and create the configuration reference.
78 6
            $this->createConfiguration($name, $this->mergeConfiguration($config, $options), $container);
79
80 6
            if (!$container->hasDefinition(MasterSlaveConnectionFactory::class) && $this->hasConnectionOption('read', $options)) {
81 6
                $container->register(MasterSlaveConnectionFactory::class, MasterSlaveConnectionFactory::class)
82 6
                    ->addTag('bdf_prime.connection_factory', ['priority' => -255])
83 6
                    ->addArgument(new Reference(ConnectionFactory::class));
84
            }
85
86 6
            if (!$container->hasDefinition(ShardingConnectionFactory::class) && $this->hasConnectionOption('shards', $options)) {
87 6
                $container->register(ShardingConnectionFactory::class, ShardingConnectionFactory::class)
88 6
                    ->addTag('bdf_prime.connection_factory', ['priority' => -256])
89 6
                    ->addArgument(new Reference(ConnectionFactory::class));
90
            }
91
        }
92
93 6
        $registry = $container->getDefinition(ConnectionRegistry::class);
94 6
        $registry->replaceArgument(0, $config['connections']);
95
    }
96
97
    /**
98
     * @param string $option
99
     * @param array $options
100
     * @return bool
101
     */
102 6
    private function hasConnectionOption(string $option, array $options): bool
103
    {
104 6
        if (isset($options[$option])) {
105 1
            return true;
106
        }
107
108 6
        if (!isset($options['url'])) {
109
            return false;
110
        }
111
112
        // The option could be in the url. Adding the factory by default.
113 6
        return true;
114
    }
115
116
    /**
117
     * @param array $config
118
     * @param ContainerBuilder $container
119
     */
120 6
    public function configureSerializer(array $config, ContainerBuilder $container)
121
    {
122 6
        if (!isset($config['serializer'])) {
123 6
            return;
124
        }
125
126
        $prime = $container->findDefinition('prime');
127
        $prime->addMethodCall('setSerializer', [new Reference($config['serializer'])]);
128
    }
129
130
    /**
131
     * @param array $config
132
     * @param ContainerBuilder $container
133
     */
134 6
    public function configureMapperCache(array $config, ContainerBuilder $container)
135
    {
136 6
        $definition = $container->getDefinition(MapperFactory::class);
137
138 6
        if (isset($config['cache']['query'])) {
139 2
            $ref = $this->createResultCacheReference('prime.cache.query', $config['cache']['query'], $container);
140
141 2
            if ($ref !== null) {
142 2
                $definition->replaceArgument(2, $ref);
143
            }
144
        }
145
146 6
        if (isset($config['cache']['metadata'])) {
147
            $ref = $this->createCacheReference('prime.cache.metadata', $config['cache']['metadata'], $container);
148
149
            if ($ref !== null) {
150
                $definition->replaceArgument(1, $ref);
151
            }
152
        }
153
    }
154
155
    /**
156
     * @param FileLoader $loader
157
     * @param ContainerBuilder $container
158
     * @param array $config
159
     * @return void
160
     * @throws \Exception
161
     */
162
    public function configureMongo(FileLoader $loader, ContainerBuilder $container, array $config): void
163
    {
164
        $loader->load('prime_mongo.yaml');
165
166
        $container->registerForAutoconfiguration(DocumentMapperInterface::class)
167
            ->setPublic(true)
168
            ->setShared(false)
169
            ->setAutowired(true);
170
171
        if (isset($config['cache']['metadata'])) {
172
            $definition = $container->findDefinition(DocumentHydratorFactory::class);
173
            $ref = $this->createCacheReference('prime.cache.metadata', $config['cache']['metadata'], $container);
174
175
            if ($ref !== null) {
176
                $definition->replaceArgument(0, $ref);
177
            }
178
        }
179
    }
180
181
    /**
182
     * @param array $config
183
     * @param ContainerBuilder $container
184
     */
185 6
    private function configureUpgrader(array $config, ContainerBuilder $container)
0 ignored issues
show
Unused Code introduced by
The parameter $config is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

185
    private function configureUpgrader(/** @scrutinizer ignore-unused */ array $config, ContainerBuilder $container)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
186
    {
187 6
        $container->register(RepositoryUpgraderResolver::class)
188 6
            ->addArgument(new Reference(ServiceLocator::class))
189
        ;
190
191 6
        $container->register(StructureUpgraderResolverAggregate::class)
192 6
            ->setPublic(true)
193 6
            ->addMethodCall('register', [new Reference(RepositoryUpgraderResolver::class)])
194
        ;
195
196 6
        $container->setAlias(StructureUpgraderResolverInterface::class, StructureUpgraderResolverAggregate::class)
197 6
            ->setPublic(true)
198
        ;
199
200 6
        $container->findDefinition('prime.upgrade_command')
201 6
            ->replaceArgument(0, new Reference(StructureUpgraderResolverInterface::class))
202
        ;
203
    }
204
205
    /**
206
     * @param array $globalConfig
207
     * @param array $config
208
     *
209
     * @return array
210
     */
211 6
    public function mergeConfiguration(array $globalConfig, array $config): array
212
    {
213
        return [
214 6
            'types' => array_merge($globalConfig['types'], $config['types']),
215 6
            'auto_commit' => $config['auto_commit'] ?? $globalConfig['auto_commit'],
216 6
            'logging' => $config['logging'] ?? $globalConfig['logging'],
217 6
            'profiling' => $config['profiling'] ?? $globalConfig['profiling'],
218
        ];
219
    }
220
221
    /**
222
     * Create and declare the configuration definition of the connection
223
     *
224
     * @param string $name
225
     * @param array $config
226
     * @param ContainerBuilder $container
227
     */
228 6
    public function createConfiguration(string $name, array $config, ContainerBuilder $container): void
229
    {
230 6
        $namespace = "prime.{$name}_connection";
231
232 6
        $configuration = $container->setDefinition("$namespace.configuration", new ChildDefinition(PrimeConfiguration::class));
233 6
        $configuration->setPublic(true);
234 6
        $configuration->addMethodCall('setTypes', [new Reference("$namespace.types")]);
235
236 6
        $typeRegistry = $container->setDefinition("$namespace.types", new ChildDefinition(TypesRegistryInterface::class));
237 6
        foreach ($config['types'] as $type => $info) {
238 2
            $typeRegistry->addMethodCall('register', [$info['class'], is_int($type) ? null : $type]);
239
        }
240
241 6
        if (isset($config['auto_commit'])) {
242 2
            $configuration->addMethodCall('setAutoCommit', [$config['auto_commit']]);
243
        }
244
245 6
        $logger = null;
246 6
        if ($config['logging']) {
247 6
            $logger = new Reference('prime.logger');
248
        }
249
250 6
        if ($config['profiling']) {
251 6
            $profilingLogger = new Reference('prime.logger.profiling');
252
253 6
            if ($logger !== null) {
254 6
                $chainLogger = $container->findDefinition('prime.logger.chain');
255 6
                $chainLogger->replaceArgument(0, [$logger, $profilingLogger]);
256
257 6
                $logger = new Reference('prime.logger.chain');
258
            } else {
259
                $logger = $profilingLogger;
260
            }
261
        }
262
263 6
        if ($logger) {
264 6
            $configuration->addMethodCall('setSQLLogger', [$logger]);
265
        }
266
    }
267
268
    /**
269
     * @param array $config
270
     * @param ContainerBuilder $container
271
     *
272
     * @return null|Reference
273
     */
274
    private function createCacheReference(string $namespace, array $config, ContainerBuilder $container)
275
    {
276
        if (isset($config['service'])) {
277
            return new Reference($config['service']);
278
        }
279
280
        if (isset($config['pool'])) {
281
            if (!$container->has($namespace)) {
282
                $definition = $container->register($namespace, Psr16Cache::class);
283
                $definition->addArgument(new Reference($config['pool']));
284
            }
285
286
            return new Reference($namespace);
287
        }
288
289
        return null;
290
    }
291
292
    /**
293
     * Create the cache result service
294
     *
295
     * @param array $config
296
     * @param ContainerBuilder $container
297
     *
298
     * @return null|Reference
299
     */
300 2
    private function createResultCacheReference(string $namespace, array $config, ContainerBuilder $container)
301
    {
302 2
        if (isset($config['service'])) {
303 1
            return new Reference($config['service']);
304
        }
305
306 1
        if (isset($config['pool'])) {
307 1
            if (!$container->has($namespace)) {
308 1
                $definition = $container->register($namespace.'.doctrine-provider', DoctrineProvider::class);
309 1
                $definition->setFactory([DoctrineProvider::class, 'wrap']);
310 1
                $definition->addArgument(new Reference($config['pool']));
311
312 1
                $definition = $container->register($namespace, DoctrineCacheAdapter::class);
313 1
                $definition->addArgument(new Reference($namespace.'.doctrine-provider'));
314
            }
315
316 1
            return new Reference($namespace);
317
        }
318
319
        return null;
320
    }
321
322
    /**
323
     * Rearrange the key name of the configuration
324
     *
325
     * @param array $options
326
     *
327
     * @return array
328
     */
329 6
    private function cleanConnectionOptions(array $options): array
330
    {
331 6
        if (isset($options['platform_service'])) {
332
            $options['platform'] = new Reference($options['platform_service']);
333
            unset($options['platform_service']);
334
        }
335
336
//        unset($options['mapping_types']);
337
338 6
        if (isset($options['shard_choser'])) {
339
            $options['shard_choser'] = new Reference($options['shard_choser']);
340
        }
341
342
        $parameters = [
343
            'options' => 'driverOptions',
344
            'driver_class' => 'driverClass',
345
            'wrapper_class' => 'wrapperClass',
346
            'shard_choser' => 'shardChoser',
347
            'distribution_key' => 'distributionKey',
348
            'server_version' => 'serverVersion',
349
            'default_table_options' => 'defaultTableOptions',
350
        ];
351
352 6
        foreach ($parameters as $old => $new) {
353 6
            if (isset($options[$old])) {
354 6
                $options[$new] = $options[$old];
355 6
                unset($options[$old]);
356
            }
357
        }
358
359 6
        if (!empty($options['read']) && !empty($options['shards'])) {
360
            throw new InvalidArgumentException('Sharding and master-slave connection cannot be used together');
361
        }
362
363
364 6
        $parameters = ['read', 'shards', 'driverOptions', 'defaultTableOptions'];
365
366 6
        foreach ($parameters as $key) {
367 6
            if (empty($options[$key])) {
368 6
                unset($options[$key]);
369
            }
370
        }
371
372 6
        return $options;
373
    }
374
375
    /**
376
     * {@inheritDoc}
377
     */
378 6
    public function getConfiguration(array $config, ContainerBuilder $container)
379
    {
380 6
        return new Configuration($container->getParameter('kernel.debug'));
381
    }
382
}
383