Completed
Push — master ( d42d86...ba3eee )
by David
08:38
created

HttplugExtension   B

Complexity

Total Complexity 46

Size/Duplication

Total Lines 370
Duplicated Lines 11.89 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 66.67%

Importance

Changes 21
Bugs 4 Features 9
Metric Value
wmc 46
c 21
b 4
f 9
lcom 1
cbo 7
dl 44
loc 370
ccs 72
cts 108
cp 0.6667
rs 8.3999

10 Methods

Rating   Name   Duplication   Size   Complexity  
B load() 0 44 6
B configureClients() 0 22 5
A configurePlugins() 0 18 4
D configurePluginByName() 0 37 10
B configureAuthentication() 0 30 6
B configureAutoDiscoveryClients() 24 38 5
B registerAutoDiscoverableClient() 0 26 2
A registerDebugPlugin() 0 13 1
A getConfiguration() 0 4 1
B configureClient() 20 70 6

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