Completed
Push — master ( 19ba56...bf992c )
by Tobias
06:15
created

configureProviderPlugins()   D

Complexity

Conditions 12
Paths 291

Size

Total Lines 59

Duplication

Lines 18
Ratio 30.51 %

Code Coverage

Tests 24
CRAP Score 21.216

Importance

Changes 0
Metric Value
dl 18
loc 59
ccs 24
cts 40
cp 0.6
rs 4.8912
c 0
b 0
f 0
cc 12
nc 291
nop 3
crap 21.216

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the BazingaGeocoderBundle package.
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 *
10
 * @license    MIT License
11
 */
12
13
namespace Bazinga\GeocoderBundle\DependencyInjection;
14
15
use Bazinga\GeocoderBundle\DataCollector\GeocoderDataCollector;
16
use Bazinga\GeocoderBundle\DependencyInjection\Compiler\FactoryValidatorPass;
17
use Bazinga\GeocoderBundle\Plugin\FakeIpPlugin;
18
use Bazinga\GeocoderBundle\Plugin\ProfilingPlugin;
19
use Bazinga\GeocoderBundle\ProviderFactory\PluginProviderFactory;
20
use Bazinga\GeocoderBundle\ProviderFactory\ProviderFactoryInterface;
21
use Geocoder\Plugin\Plugin\CachePlugin;
22
use Geocoder\Plugin\Plugin\LimitPlugin;
23
use Geocoder\Plugin\Plugin\LocalePlugin;
24
use Geocoder\Plugin\Plugin\LoggerPlugin;
25
use Geocoder\Plugin\PluginProvider;
26
use Symfony\Component\Config\Definition\Processor;
27
use Symfony\Component\Config\FileLocator;
28
use Symfony\Component\DependencyInjection\ContainerBuilder;
29
use Symfony\Component\DependencyInjection\Exception\ServiceNotFoundException;
30
use Symfony\Component\DependencyInjection\Loader\YamlFileLoader;
31
use Symfony\Component\DependencyInjection\Reference;
32
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
33
34
/**
35
 * William Durand <[email protected]>.
36
 */
37
class BazingaGeocoderExtension extends Extension
38
{
39 26
    public function load(array $configs, ContainerBuilder $container)
40
    {
41 26
        $processor = new Processor();
42 26
        $configuration = $this->getConfiguration($configs, $container);
43 26
        $config = $processor->processConfiguration($configuration, $configs);
0 ignored issues
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 42 can be null; however, Symfony\Component\Config...:processConfiguration() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
44
45 26
        $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
46 26
        $loader->load('services.yml');
47
48 26
        if (true === $config['profiling']['enabled']) {
49 2
            $loader->load('profiling.yml');
50
        }
51
52 26
        if ($config['fake_ip']['enabled']) {
53
            $definition = $container->getDefinition(FakeIpPlugin::class);
54
            $definition->replaceArgument(1, $config['fake_ip']['ip']);
55
        } else {
56 26
            $container->removeDefinition(FakeIpPlugin::class);
57
        }
58
59 26
        $this->loadProviders($container, $config);
60 26
    }
61
62 26
    private function loadProviders(ContainerBuilder $container, array $config)
63
    {
64 26
        foreach ($config['providers'] as $providerName => $providerConfig) {
65
            try {
66 25
                $factoryService = $container->getDefinition($providerConfig['factory']);
67 25
                $factoryClass = $factoryService->getClass() ?: $providerConfig['factory'];
68 25
                if (!$this->implementsPoviderFactory($factoryClass)) {
69
                    throw new \LogicException(sprintf('Provider factory "%s" must implement ProviderFactoryInterface', $providerConfig['factory']));
70
                }
71
                // See if any option has a service reference
72 25
                $providerConfig['options'] = $this->findReferences($providerConfig['options']);
73 25
                $factoryClass::validate($providerConfig['options'], $providerName);
74
            } catch (ServiceNotFoundException $e) {
75
                // Assert: We are using a custom factory. If invalid config, it will be caught in FactoryValidatorPass
76
                $providerConfig['options'] = $this->findReferences($providerConfig['options']);
77
                FactoryValidatorPass::addFactoryServiceId($providerConfig['factory']);
78
            }
79
80 25
            $serviceId = 'bazinga_geocoder.provider.'.$providerName;
81 25
            $plugins = $this->configureProviderPlugins($container, $providerConfig, $serviceId);
82
83 25
            $def = $container->register($serviceId, PluginProvider::class)
84 25
                ->setFactory([PluginProviderFactory::class, 'createPluginProvider'])
85 25
                ->addArgument($plugins)
86 25
                ->addArgument(new Reference($providerConfig['factory']))
87 25
                ->addArgument($providerConfig['options']);
88
89 25
            $def->addTag('bazinga_geocoder.provider');
90 25
            foreach ($providerConfig['aliases'] as $alias) {
91 25
                $container->setAlias($alias, $serviceId);
92
            }
93
        }
94 26
    }
95
96
    /**
97
     * Configure plugins for a client.
98
     *
99
     * @param ContainerBuilder $container
100
     * @param array            $config
101
     * @param string           $providerServiceId
102
     *
103
     * @return array
104
     */
105 25
    public function configureProviderPlugins(ContainerBuilder $container, array $config, string $providerServiceId): array
106
    {
107 25
        $plugins = [];
108 25
        foreach ($config['plugins'] as $plugin) {
109 2
            if ($plugin['reference']['enabled']) {
110 2
                $plugins[] = $plugin['reference']['id'];
111
            }
112
        }
113
114 25
        if (isset($config['cache']) || isset($config['cache_lifetime'])) {
115 1
            if (null === $cacheServiceId = $config['cache']) {
116
                if (!$container->has('app.cache')) {
117
                    throw new \LogicException('You need to specify a service for cache.');
118
                }
119
                $cacheServiceId = 'app.cache';
120
            }
121 1
            $plugins[] = $providerServiceId.'.cache';
122 1
            $container->register($providerServiceId.'.cache', CachePlugin::class)
123 1
                ->setPublic(false)
124 1
                ->setArguments([new Reference($cacheServiceId), (int) $config['cache_lifetime']]);
125
        }
126
127 25 View Code Duplication
        if (isset($config['limit'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
128
            $plugins[] = $providerServiceId.'.limit';
129
            $container->register($providerServiceId.'.limit', LimitPlugin::class)
130
                ->setPublic(false)
131
                ->setArguments([(int) $config['limit']]);
132
        }
133
134 25 View Code Duplication
        if (isset($config['locale'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
135
            $plugins[] = $providerServiceId.'.locale';
136
            $container->register($providerServiceId.'.locale', LocalePlugin::class)
137
                ->setPublic(false)
138
                ->setArguments([$config['locale']]);
139
        }
140
141 25 View Code Duplication
        if (isset($config['logger'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
142
            $plugins[] = $providerServiceId.'.logger';
143
            $container->register($providerServiceId.'.logger', LoggerPlugin::class)
144
                ->setPublic(false)
145
                ->setArguments([new Reference($config['logger'])]);
146
        }
147
148 25
        if ($container->has(FakeIpPlugin::class)) {
149
            $plugins[] = FakeIpPlugin::class;
150
        }
151
152 25
        if ($container->has(GeocoderDataCollector::class)) {
153 1
            $plugins[] = $providerServiceId.'.profiler';
154 1
            $container->register($providerServiceId.'.profiler', ProfilingPlugin::class)
155 1
                ->setPublic(false)
156 1
                ->setArguments([substr($providerServiceId, strlen('bazinga_geocoder.provider.'))])
157 1
                ->addTag('bazinga_geocoder.profiling_plugin');
158
        }
159
160 25
        return array_map(function (string $id) {
161 3
            return new Reference($id);
162 25
        }, $plugins);
163
    }
164
165
    /**
166
     * {@inheritdoc}
167
     */
168 26
    public function getConfiguration(array $config, ContainerBuilder $container)
169
    {
170 26
        return new Configuration($container->getParameter('kernel.debug'));
171
    }
172
173
    /**
174
     * @param array $options
175
     *
176
     * @return array
177
     */
178 25
    private function findReferences(array $options): array
179
    {
180 25
        foreach ($options as $key => $value) {
181 18
            if (is_array($value)) {
182 1
                $options[$key] = $this->findReferences($value);
183 18
            } elseif ('_service' === substr((string) $key, -8) || 0 === strpos((string) $value, '@') || 'service' === $key) {
184 18
                $options[$key] = new Reference(ltrim($value, '@'));
185
            }
186
        }
187
188 25
        return $options;
189
    }
190
191
    /**
192
     * @param mixed $factoryClass
193
     *
194
     * @return bool
195
     */
196 25
    private function implementsPoviderFactory($factoryClass): bool
197
    {
198 25
        if (false === $interfaces = class_implements($factoryClass)) {
199
            return false;
200
        }
201
202 25
        return in_array(ProviderFactoryInterface::class, $interfaces);
203
    }
204
}
205