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

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