Completed
Pull Request — master (#87)
by Tobias
10:23
created

HttplugExtension   B

Complexity

Total Complexity 52

Size/Duplication

Total Lines 326
Duplicated Lines 3.68 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 59.51%

Importance

Changes 17
Bugs 3 Features 7
Metric Value
wmc 52
c 17
b 3
f 7
lcom 1
cbo 6
dl 12
loc 326
rs 7.9487
ccs 25
cts 42
cp 0.5951

9 Methods

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