Passed
Pull Request — master (#3)
by Vincent
08:42
created

PrimeExtension::cleanConnectionOptions()   B

Complexity

Conditions 9
Paths 48

Size

Total Lines 44
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 9.8886

Importance

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