Completed
Push — master ( c516a4...1e806a )
by Márk
07:39
created

HttplugExtension   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 307
Duplicated Lines 3.91 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 60.47%

Importance

Changes 16
Bugs 2 Features 7
Metric Value
wmc 47
c 16
b 2
f 7
lcom 1
cbo 6
dl 12
loc 307
ccs 26
cts 43
cp 0.6047
rs 8.439

9 Methods

Rating   Name   Duplication   Size   Complexity  
A configurePlugins() 0 18 4
D configurePluginByName() 0 37 10
B configureAuthentication() 0 30 6
B configureClient() 12 49 5
A registerDebugPlugin() 0 10 1
C load() 0 40 8
C configureClients() 0 29 7
B configureAutoDiscoveryClients() 0 20 5
A registerAutoDiscoverableClientWithDebugPlugin() 0 15 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like HttplugExtension 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 HttplugExtension, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Http\HttplugBundle\DependencyInjection;
4
5
use Http\Client\Common\FlexibleHttpClient;
6
use Http\Client\Common\HttpMethodsClient;
7
use Http\Client\Common\Plugin\AuthenticationPlugin;
8
use Http\Client\Common\PluginClient;
9
use Http\Discovery\HttpAsyncClientDiscovery;
10
use Http\HttplugBundle\ClientFactory\DummyClient;
11
use Http\HttplugBundle\Collector\DebugPlugin;
12
use Http\Message\Authentication\BasicAuth;
13
use Http\Message\Authentication\Bearer;
14
use Http\Message\Authentication\Wsse;
15
use Symfony\Component\Config\FileLocator;
16
use Symfony\Component\DependencyInjection\ContainerBuilder;
17
use Symfony\Component\DependencyInjection\Definition;
18
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
19
use Symfony\Component\DependencyInjection\Reference;
20
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
21
22
/**
23
 * @author David Buchmann <[email protected]>
24
 * @author Tobias Nyholm <[email protected]>
25
 */
26
class HttplugExtension extends Extension
27
{
28
    /**
29
     * {@inheritdoc}
30
     */
31 3
    public function load(array $configs, ContainerBuilder $container)
32
    {
33 3
        $configuration = $this->getConfiguration($configs, $container);
34 3
        $config = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Documentation introduced by
$configuration is of type object|null, but the function expects a object<Symfony\Component...ConfigurationInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
35
36 3
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
37
38 3
        $loader->load('services.xml');
39 3
        $loader->load('plugins.xml');
40
41 3
        $enabled = is_bool($config['toolbar']['enabled']) ? $config['toolbar']['enabled'] : $container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug');
42 3
        if ($enabled) {
43
            $loader->load('data-collector.xml');
44
            $config['_inject_collector_plugin'] = true;
45
46
            if (!empty($config['toolbar']['formatter'])) {
47
                // Add custom formatter
48
                $container->getDefinition('httplug.collector.debug_collector')
49
                    ->replaceArgument(0, new Reference($config['toolbar']['formatter']));
50
            }
51
52
            $container->getDefinition('httplug.formatter.full_http_message')
53
                ->addArgument($config['toolbar']['captured_body_length']);
54
        }
55
56 3
        foreach ($config['classes'] as $service => $class) {
57 3
            if (!empty($class)) {
58 1
                $container->register(sprintf('httplug.%s.default', $service), $class);
59 1
            }
60 3
        }
61
62
        // Set main aliases
63 3
        foreach ($config['main_alias'] as $type => $id) {
64 3
            $container->setAlias(sprintf('httplug.%s', $type), $id);
65 3
        }
66
67 3
        $this->configurePlugins($container, $config['plugins']);
68 3
        $this->configureClients($container, $config);
69 3
        $this->configureAutoDiscoveryClients($container, $config);
70 3
    }
71
72
    /**
73
     * Configure client services.
74
     *
75
     * @param ContainerBuilder $container
76
     * @param array            $config
77
     */
78 3
    private function configureClients(ContainerBuilder $container, array $config)
79
    {
80
        // If we have a client named 'default'
81 3
        $first = isset($config['clients']['default']) ? 'default' : null;
82
83 3
        foreach ($config['clients'] as $name => $arguments) {
84
            if ($first === null) {
85
                // Save the name of the first configurated client.
86
                $first = $name;
87
            }
88
89
            $this->configureClient($container, $name, $arguments, $config['_inject_collector_plugin']);
90 3
        }
91
92
        // If we have clients configured
93 3
        if ($first !== null) {
94
            if ($first !== 'default') {
95
                // Alias the first client to httplug.client.default
96
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
97
            }
98 3
        } elseif (isset($config['_inject_collector_plugin'])) {
99
            $serviceIdDebugPlugin = $this->registerDebugPlugin($container, 'default');
100
            // No client was configured. Make sure to configure the auto discovery client with the PluginClient.
101
            $container->register('httplug.client', PluginClient::class)
102
                ->addArgument(new Reference('httplug.client.default'))
103
                ->addArgument([])
104
                ->addArgument(['debug_plugins' => [new Reference($serviceIdDebugPlugin)]]);
105
        }
106
    }
107
108
    /**
109
     * @param ContainerBuilder $container
110
     * @param array            $config
111
     */
112
    private function configurePlugins(ContainerBuilder $container, array $config)
113
    {
114
        if (!empty($config['authentication'])) {
115
            $this->configureAuthentication($container, $config['authentication']);
116
        }
117
        unset($config['authentication']);
118
119
        foreach ($config as $name => $pluginConfig) {
120
            $pluginId = 'httplug.plugin.'.$name;
121
122
            if ($pluginConfig['enabled']) {
123
                $def = $container->getDefinition($pluginId);
124
                $this->configurePluginByName($name, $def, $pluginConfig);
125
            } else {
126
                $container->removeDefinition($pluginId);
127
            }
128
        }
129
    }
130
131
    /**
132
     * @param string     $name
133
     * @param Definition $definition
134
     * @param array      $config
135
     */
136
    private function configurePluginByName($name, Definition $definition, array $config)
137
    {
138
        switch ($name) {
139
            case 'cache':
140
                $definition
141
                    ->replaceArgument(0, new Reference($config['cache_pool']))
142
                    ->replaceArgument(1, new Reference($config['stream_factory']))
143
                    ->replaceArgument(2, $config['config']);
144
                break;
145
            case 'cookie':
146
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
147
                break;
148
            case 'decoder':
149
                $definition->addArgument($config['use_content_encoding']);
150
                break;
151
            case 'history':
152
                $definition->replaceArgument(0, new Reference($config['journal']));
153
                break;
154
            case 'logger':
155
                $definition->replaceArgument(0, new Reference($config['logger']));
156
                if (!empty($config['formatter'])) {
157
                    $definition->replaceArgument(1, new Reference($config['formatter']));
158
                }
159
                break;
160
            case 'redirect':
161
                $definition
162
                    ->addArgument($config['preserve_header'])
163
                    ->addArgument($config['use_default_for_multiple']);
164
                break;
165
            case 'retry':
166
                $definition->addArgument($config['retry']);
167
                break;
168
            case 'stopwatch':
169
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
170
                break;
171
        }
172
    }
173
174
    /**
175
     * @param ContainerBuilder $container
176
     * @param array            $config
177
     */
178
    private function configureAuthentication(ContainerBuilder $container, array $config)
179
    {
180
        foreach ($config as $name => $values) {
181
            $authServiceKey = sprintf('httplug.plugin.authentication.%s.auth', $name);
182
            switch ($values['type']) {
183
                case 'bearer':
184
                    $container->register($authServiceKey, Bearer::class)
185
                        ->addArgument($values['token']);
186
                    break;
187
                case 'basic':
188
                    $container->register($authServiceKey, BasicAuth::class)
189
                        ->addArgument($values['username'])
190
                        ->addArgument($values['password']);
191
                    break;
192
                case 'wsse':
193
                    $container->register($authServiceKey, Wsse::class)
194
                        ->addArgument($values['username'])
195
                        ->addArgument($values['password']);
196
                    break;
197
                case 'service':
198
                    $authServiceKey = $values['service'];
199
                    break;
200
                default:
201
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
202
            }
203
204
            $container->register('httplug.plugin.authentication.'.$name, AuthenticationPlugin::class)
205
                ->addArgument(new Reference($authServiceKey));
206
        }
207
    }
208
209
    /**
210
     * @param ContainerBuilder $container
211
     * @param string           $name
212
     * @param array            $arguments
213
     * @param bool             $enableCollector
214
     */
215
    private function configureClient(ContainerBuilder $container, $name, array $arguments, $enableCollector)
216
    {
217
        $serviceId = 'httplug.client.'.$name;
218
        $def = $container->register($serviceId, DummyClient::class);
219
220
        // If there is no plugins nor should we use the data collector
221
        if (empty($arguments['plugins']) && !$enableCollector) {
222
            $def->setFactory([new Reference($arguments['factory']), 'createClient'])
223
                ->addArgument($arguments['config']);
224
        } else {
225
            $serviceIdDebugPlugin = $this->registerDebugPlugin($container, $name);
226
227
            $def->setFactory('Http\HttplugBundle\ClientFactory\PluginClientFactory::createPluginClient')
228
                ->addArgument(
229
                    array_map(
230
                        function ($id) {
231
                            return new Reference($id);
232
                        },
233
                        $arguments['plugins']
234
                    )
235
                )
236
                ->addArgument(new Reference($arguments['factory']))
237
                ->addArgument($arguments['config'])
238
                ->addArgument(['debug_plugins' => [new Reference($serviceIdDebugPlugin)]]);
239
240
            // tell the plugin journal what plugins we used
241
            $container->getDefinition('httplug.collector.plugin_journal')
242
                ->addMethodCall('setPlugins', [$name, $arguments['plugins']]);
243
        }
244
245
246
        /*
247
         * Decorate the client with clients from client-common
248
         */
249
250 View Code Duplication
        if ($arguments['flexible_client']) {
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...
251
            $container->register($serviceId.'.flexible', FlexibleHttpClient::class)
252
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
253
                ->setPublic(false)
254
                ->setDecoratedService($serviceId);
255
        }
256
257 View Code Duplication
        if ($arguments['http_methods_client']) {
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...
258
            $container->register($serviceId.'.http_methods', HttpMethodsClient::class)
259
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
260
                ->setPublic(false)
261
                ->setDecoratedService($serviceId);
262
        }
263
    }
264
265
    /**
266
     * Create a new plugin service for this client.
267
     *
268
     * @param ContainerBuilder $container
269
     * @param string           $name
270
     *
271
     * @return string
272
     */
273
    private function registerDebugPlugin(ContainerBuilder $container, $name)
274
    {
275
        $serviceIdDebugPlugin = 'httplug.client.'.$name.'.debug_plugin';
276
        $container->register($serviceIdDebugPlugin, DebugPlugin::class)
277
            ->addArgument(new Reference('httplug.collector.debug_collector'))
278
            ->addArgument($name)
279
            ->setPublic(false);
280
281
        return $serviceIdDebugPlugin;
282
    }
283
284
    /**
285
     * Make sure we inject the debug plugin for clients found by auto discovery.
286
     *
287
     * @param ContainerBuilder $container
288
     * @param array            $config
289
     */
290
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config)
291
    {
292
        $httpClient = $config['discovery']['client'];
293
        if ($httpClient === 'auto') {
294
            $httpClient = $this->registerAutoDiscoverableClientWithDebugPlugin($container, 'client');
295
        } elseif ($httpClient) {
296
            $httpClient = new Reference($httpClient);
297
        }
298
299
        $asyncHttpClient = $config['discovery']['async_client'];
300
        if ($asyncHttpClient === 'auto') {
301
            $asyncHttpClient = $this->registerAutoDiscoverableClientWithDebugPlugin($container, 'async_client');
302
        } elseif ($asyncHttpClient) {
303
            $asyncHttpClient = new Reference($httpClient);
304
        }
305
306
        $container->getDefinition('httplug.strategy')
307
            ->addArgument($httpClient)
308
            ->addArgument($asyncHttpClient);
309
    }
310
311
    /**
312
     * @param ContainerBuilder $container
313
     * @param $name
314
     *
315
     * @return Reference
316
     */
317
    private function registerAutoDiscoverableClientWithDebugPlugin(ContainerBuilder $container, $name)
318
    {
319
        $definition = $container->register('httplug.auto_discovery_'.$name.'.pure', DummyClient::class);
320
        $definition->setPublic(false);
321
        $definition->setFactory([HttpAsyncClientDiscovery::class, 'find']);
322
323
        $serviceIdDebugPlugin = $this->registerDebugPlugin($container, 'auto_discovery_'.$name);
324
        $container->register('httplug.auto_discovery_'.$name.'.plugin', PluginClient::class)
325
            ->setPublic(false)
326
            ->addArgument(new Reference('httplug.auto_discovery_'.$name.'.pure'))
327
            ->addArgument([])
328
            ->addArgument(['debug_plugins' => [new Reference($serviceIdDebugPlugin)]]);
329
330
        return new Reference('httplug.auto_discovery_'.$name.'.plugin');
331
    }
332
}
333