Completed
Push — fix-service-decoration ( 148fe3 )
by Lukas Kahwe
03:15 queued 01:56
created

LiipMonitorExtension   D

Complexity

Total Complexity 69

Size/Duplication

Total Lines 345
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Importance

Changes 0
Metric Value
wmc 69
lcom 1
cbo 14
dl 0
loc 345
rs 4.2465
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
C setParameters() 0 68 33
B configureDoctrineMigrationsCheck() 0 26 6
A getPredefinedMigrations() 0 17 3
C load() 0 69 13
F createMigrationConfigurationService() 0 80 12
B createTemporaryConfiguration() 0 31 2

How to fix   Complexity   

Complex Class

Complex classes like LiipMonitorExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use LiipMonitorExtension, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Liip\MonitorBundle\DependencyInjection;
4
5
use Doctrine\DBAL\Connection;
6
use Doctrine\DBAL\Driver\PDOSqlite\Driver;
7
use Doctrine\DBAL\Migrations\Configuration\AbstractFileConfiguration;
8
use Doctrine\DBAL\Migrations\Configuration\Configuration as MigrationConfiguration;
9
use Symfony\Component\Config\FileLocator;
10
use Symfony\Component\DependencyInjection\ChildDefinition;
11
use Symfony\Component\DependencyInjection\ContainerBuilder;
12
use Symfony\Component\DependencyInjection\DefinitionDecorator;
13
use Symfony\Component\DependencyInjection\Loader;
14
use Symfony\Component\DependencyInjection\Reference;
15
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
16
17
class LiipMonitorExtension extends Extension
18
{
19
    /**
20
     * Loads the services based on your application configuration.
21
     *
22
     * @param array            $configs
23
     * @param ContainerBuilder $container
24
     */
25
    public function load(array $configs, ContainerBuilder $container)
26
    {
27
        $loader = new Loader\XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
28
        $loader->load('runner.xml');
29
        $loader->load('helper.xml');
30
31
        $configuration = new Configuration();
32
        $config = $this->processConfiguration($configuration, $configs);
33
34
        if (null === $config['view_template']) {
35
            $config['view_template'] = __DIR__.'/../Resources/views/health/index.html.php';
36
        }
37
38
        if ($config['enable_controller']) {
39
            $container->setParameter(sprintf('%s.view_template', $this->getAlias()), $config['view_template']);
40
            $loader->load('controller.xml');
41
        }
42
43
        if ($config['mailer']['enabled']) {
44
            $loader->load('helper/swift_mailer.xml');
45
46
            foreach ($config['mailer'] as $key => $value) {
47
                $container->setParameter(sprintf('%s.mailer.%s', $this->getAlias(), $key), $value);
48
            }
49
        }
50
51
        $container->setParameter(sprintf('%s.default_group', $this->getAlias()), $config['default_group']);
52
53
        // symfony3 does not define templating.helper.assets unless php templating is included
54
        if ($container->has('templating.helper.assets')) {
55
            $pathHelper = $container->getDefinition('liip_monitor.helper');
56
            $pathHelper->replaceArgument(0, 'templating.helper.assets');
57
        }
58
59
        // symfony3 does not define templating.helper.router unless php templating is included
60
        if ($container->has('templating.helper.router')) {
61
            $pathHelper = $container->getDefinition('liip_monitor.helper');
62
            $pathHelper->replaceArgument(1, 'templating.helper.router');
63
        }
64
65
        if (empty($config['checks'])) {
66
            return;
67
        }
68
69
        $checksLoaded = array();
70
        $containerParams = array();
71
        foreach ($config['checks']['groups'] as $group => $checks) {
72
            if (empty($checks)) {
73
                continue;
74
            }
75
76
            foreach ($checks as $check => $values) {
77
                if (empty($values)) {
78
                    continue;
79
                }
80
81
                $containerParams['groups'][$group][$check] = $values;
82
                $this->setParameters($container, $check, $group, $values);
83
84
                if (!in_array($check, $checksLoaded)) {
85
                    $loader->load('checks/'.$check.'.xml');
86
                    $checksLoaded[] = $check;
87
                }
88
            }
89
        }
90
91
        $container->setParameter(sprintf('%s.checks', $this->getAlias()), $containerParams);
92
        $this->configureDoctrineMigrationsCheck($container, $containerParams);
93
    }
94
95
    /**
96
     * @param ContainerBuilder $container
97
     * @param string           $checkName
98
     * @param string           $group
99
     * @param array            $values
100
     */
101
    private function setParameters(ContainerBuilder $container, $checkName, $group, $values)
102
    {
103
        $prefix = sprintf('%s.check.%s', $this->getAlias(), $checkName);
104
        switch ($checkName) {
105
            case 'class_exists':
106
            case 'cpu_performance':
107
            case 'php_extensions':
108
            case 'php_version':
109
            case 'php_flags':
110
            case 'readable_directory':
111
            case 'writable_directory':
112
            case 'process_running':
113
            case 'doctrine_dbal':
114
            case 'doctrine_mongodb':
115
            case 'http_service':
116
            case 'guzzle_http_service':
117
            case 'memcache':
118
            case 'redis':
119
            case 'rabbit_mq':
120
            case 'stream_wrapper_exists':
121
            case 'file_ini':
122
            case 'file_json':
123
            case 'file_xml':
124
            case 'file_yaml':
125
            case 'expressions':
126
                $container->setParameter($prefix.'.'.$group, $values);
127
                continue;
128
129
            case 'symfony_version':
130
                continue;
131
132
            case 'opcache_memory':
133
                if (!class_exists('ZendDiagnostics\Check\OpCacheMemory')) {
134
                    throw new \InvalidArgumentException('Please require at least "v1.0.4" of "ZendDiagnostics"');
135
                }
136
                continue;
137
138
            case 'doctrine_migrations':
139
                if (!class_exists('ZendDiagnostics\Check\DoctrineMigration')) {
140
                    throw new \InvalidArgumentException('Please require at least "v1.0.6" of "ZendDiagnostics"');
141
                }
142
143
                if (!class_exists('Doctrine\Bundle\MigrationsBundle\Command\DoctrineCommand')) {
144
                    throw new \InvalidArgumentException('Please require at least "v1.0.0" of "DoctrineMigrationsBundle"');
145
                }
146
147
                if (!class_exists('Doctrine\DBAL\Migrations\Configuration\Configuration')) {
148
                    throw new \InvalidArgumentException('Please require at least "v1.1.0" of "Doctrine Migrations Library"');
149
                }
150
151
                $container->setParameter($prefix.'.'.$group, $values);
152
                continue;
153
154
            case 'pdo_connections':
155
                if (!class_exists('ZendDiagnostics\Check\PDOCheck')) {
156
                    throw new \InvalidArgumentException('Please require at least "v1.0.5" of "ZendDiagnostics"');
157
                }
158
                $container->setParameter($prefix.'.'.$group, $values);
159
                continue;
160
161
        }
162
163
        if (is_array($values)) {
164
            foreach ($values as $key => $value) {
165
                $container->setParameter($prefix.'.'.$key.'.'.$group, $value);
166
            }
167
        }
168
    }
169
170
    /**
171
     * Set up doctrine migration configuration services
172
     *
173
     * @param ContainerBuilder $container The container
174
     * @param array            $params    Container params
175
     *
176
     * @return void
177
     */
178
    private function configureDoctrineMigrationsCheck(ContainerBuilder $container, array $params)
179
    {
180
        if (!$container->hasDefinition('liip_monitor.check.doctrine_migrations') || !isset($params['groups'])) {
181
            return;
182
        }
183
184
        foreach ($params['groups'] as $groupName => $groupChecks) {
185
            if (!isset($groupChecks['doctrine_migrations'])) {
186
                continue;
187
            }
188
189
            $services = [];
190
            foreach ($groupChecks['doctrine_migrations'] as $key => $config) {
191
                $serviceConfiguration =
192
                    $this->createMigrationConfigurationService($container, $config['configuration_file'], $config[ 'connection' ]);
193
194
                $serviceId = sprintf('liip_monitor.check.doctrine_migrations.configuration.%s.%s', $groupName, $key);
195
                $container->setDefinition($serviceId, $serviceConfiguration);
196
197
                $services[$key] = $serviceId;
198
            }
199
200
            $parameter = sprintf('%s.check.%s.%s', $this->getAlias(), 'doctrine_migrations', $groupName);
201
            $container->setParameter($parameter, $services);
202
        }
203
    }
204
205
    /**
206
     * Return key-value array with migration version as key and class as a value defined in config file
207
     *
208
     * @param AbstractFileConfiguration $config Current configuration
209
     * @param Connection                $connection Fake connections
210
     *
211
     * @return array[]
212
     */
213
    private function getPredefinedMigrations(AbstractFileConfiguration $config, Connection $connection)
214
    {
215
        $result = array();
216
217
        $diff = new MigrationConfiguration($connection);
218
        $diff->setMigrationsNamespace($config->getMigrationsNamespace());
219
        $diff->setMigrationsDirectory($config->getMigrationsDirectory());
220
        foreach ($config->getMigrations() as $version) {
221
            $result[$version->getVersion()] = get_class($version->getMigration());
222
        }
223
224
        foreach ($diff->getAvailableVersions() as $version) {
225
            unset($result[$version]);
226
        }
227
228
        return $result;
229
    }
230
231
    /**
232
     * Creates migration configuration service definition
233
     *
234
     * @param ContainerBuilder $container      DI Container
235
     * @param string           $filename       File name with migration configuration
236
     * @param string           $connectionName Connection name for container service
237
     *
238
     * @return DefinitionDecorator|ChildDefinition
239
     */
240
    private function createMigrationConfigurationService(ContainerBuilder $container, $filename, $connectionName)
241
    {
242
        /** @var AbstractFileConfiguration $configuration */
243
        $connection    = new Connection([], new Driver()); // needed for correct migration loading
244
        $configuration = $this->createTemporaryConfiguration($container, $connection, $filename);
245
246
        $configurationServiceName = 'liip_monitor.check.doctrine_migrations.abstract_configuration';
247
        $serviceConfiguration = class_exists('Symfony\Component\DependencyInjection\ChildDefinition')
248
            ? new ChildDefinition($configurationServiceName)
249
            : new DefinitionDecorator($configurationServiceName)
250
        ;
251
252
        $serviceConfiguration->replaceArgument(
253
            0,
254
            new Reference(sprintf('doctrine.dbal.%s_connection', $connectionName))
255
        );
256
257
        if ($configuration->getMigrationsNamespace()) {
258
            $serviceConfiguration->addMethodCall(
259
                'setMigrationsNamespace',
260
                [ $configuration->getMigrationsNamespace() ]
261
            );
262
        }
263
264
        if ($configuration->getMigrationsTableName()) {
265
            $serviceConfiguration->addMethodCall(
266
                'setMigrationsTableName',
267
                [ $configuration->getMigrationsTableName() ]
268
            );
269
        }
270
271
        if ($configuration->getMigrationsColumnName()) {
272
            $serviceConfiguration->addMethodCall(
273
                'setMigrationsColumnName',
274
                [ $configuration->getMigrationsColumnName() ]
275
            );
276
        }
277
278
        if ($configuration->getName()) {
279
            $serviceConfiguration->addMethodCall('setName', [ $configuration->getName() ]);
280
        }
281
282
        if ($configuration->getMigrationsDirectory()) {
283
            $directory        = $configuration->getMigrationsDirectory();
284
            $pathPlaceholders = array('kernel.root_dir', 'kernel.cache_dir', 'kernel.logs_dir');
285
            foreach ($pathPlaceholders as $parameter) {
286
                $kernelDir = realpath($container->getParameter($parameter));
287
                if (strpos(realpath($directory), $kernelDir) === 0) {
288
                    $directory = str_replace($kernelDir, "%{$parameter}%", $directory);
289
                    break;
290
                }
291
            }
292
293
294
            $serviceConfiguration->addMethodCall(
295
                'setMigrationsDirectory',
296
                [ $directory ]
297
            );
298
        }
299
300
        /** @var AbstractFileConfiguration $diff */
301
        $versions = $this->getPredefinedMigrations($configuration, $connection);
302
        if ($versions) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $versions of type array[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
303
            $serviceConfiguration->addMethodCall('registerMigrations', [ $versions ]);
304
        }
305
306
        $serviceConfiguration->addMethodCall('configure', []);
307
308
        if ($configuration->areMigrationsOrganizedByYear()) {
309
            $serviceConfiguration->addMethodCall('setMigrationsAreOrganizedByYear', [ true ]);
310
311
            return $serviceConfiguration;
312
        } elseif ($configuration->areMigrationsOrganizedByYearAndMonth()) {
313
            $serviceConfiguration->addMethodCall('setMigrationsAreOrganizedByYearAndMonth', [ true ]);
314
315
            return $serviceConfiguration;
316
        }
317
318
        return $serviceConfiguration;
319
    }
320
321
    /**
322
     * Creates in-memory migration configuration for setting up container service
323
     *
324
     * @param ContainerBuilder $container  The container
325
     * @param Connection       $connection Fake connection
326
     * @param string           $filename   Migrations configuration file
327
     *
328
     * @return AbstractFileConfiguration
329
     */
330
    private function createTemporaryConfiguration(ContainerBuilder $container, Connection $connection, $filename)
331
    {
332
        // -------
333
        // This part must be in sync with Doctrine\DBAL\Migrations\Tools\Console\Helper\ConfigurationHelper::loadConfig
334
        $map = [
335
            'xml'  => '\XmlConfiguration',
336
            'yaml' => '\YamlConfiguration',
337
            'yml'  => '\YamlConfiguration',
338
            'php'  => '\ArrayConfiguration',
339
            'json' => '\JsonConfiguration',
340
        ];
341
        // --------
342
343
        $filename = $container->getParameterBag()->resolveValue($filename);
344
        $info     = pathinfo($filename);
345
        // check we can support this file type
346
        if (empty($map[ $info[ 'extension' ] ])) {
347
            throw new \InvalidArgumentException('Given config file type is not supported');
348
        }
349
350
        $class = 'Doctrine\DBAL\Migrations\Configuration';
351
        $class .= $map[ $info[ 'extension' ] ];
352
        // -------
353
354
        /** @var AbstractFileConfiguration $configuration */
355
        $configuration = new $class($connection);
356
        $configuration->load($filename);
357
        $configuration->validate();
358
359
        return $configuration;
360
    }
361
}
362