Completed
Push — master ( 75c85e...5c4db9 )
by David
08:49
created

HttplugExtension::configurePluginByName()   D

Complexity

Conditions 18
Paths 18

Size

Total Lines 92
Code Lines 65

Duplication

Lines 18
Ratio 19.57 %

Code Coverage

Tests 48
CRAP Score 23.0625

Importance

Changes 0
Metric Value
dl 18
loc 92
ccs 48
cts 64
cp 0.75
rs 4.7996
c 0
b 0
f 0
cc 18
eloc 65
nc 18
nop 5
crap 23.0625

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
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\Client\Common\PluginClient;
10
use Http\Client\Common\PluginClientFactory;
11
use Http\Client\HttpClient;
12
use Http\Message\Authentication\BasicAuth;
13
use Http\Message\Authentication\Bearer;
14
use Http\Message\Authentication\Wsse;
15
use Http\Mock\Client as MockClient;
16
use Psr\Http\Message\UriInterface;
17
use Symfony\Component\Config\FileLocator;
18
use Symfony\Component\DependencyInjection\Alias;
19
use Symfony\Component\DependencyInjection\ChildDefinition;
20
use Symfony\Component\DependencyInjection\ContainerBuilder;
21
use Symfony\Component\DependencyInjection\Definition;
22
use Symfony\Component\DependencyInjection\DefinitionDecorator;
23
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
24
use Symfony\Component\DependencyInjection\Reference;
25
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
26
27
/**
28
 * @author David Buchmann <[email protected]>
29
 * @author Tobias Nyholm <[email protected]>
30
 */
31
class HttplugExtension extends Extension
32
{
33
    /**
34
     * {@inheritdoc}
35
     */
36 13
    public function load(array $configs, ContainerBuilder $container)
37
    {
38 13
        $configuration = $this->getConfiguration($configs, $container);
39 13
        $config = $this->processConfiguration($configuration, $configs);
40
41 13
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
42
43 13
        $loader->load('services.xml');
44 13
        $loader->load('plugins.xml');
45 13
        if (\class_exists(MockClient::class)) {
46 13
            $loader->load('mock-client.xml');
47
        }
48
49
        // Register default services
50 13
        foreach ($config['classes'] as $service => $class) {
51 13
            if (!empty($class)) {
52 13
                $container->register(sprintf('httplug.%s.default', $service), $class);
53
            }
54
        }
55
56
        // Set main aliases
57 13
        foreach ($config['main_alias'] as $type => $id) {
58 13
            $container->setAlias(sprintf('httplug.%s', $type), new Alias($id, true));
59
        }
60
61
        // Configure toolbar
62 13
        $profilingEnabled = $this->isConfigEnabled($container, $config['profiling']);
63 13
        if ($profilingEnabled) {
64 12
            $loader->load('data-collector.xml');
65
66 12
            if (!empty($config['profiling']['formatter'])) {
67
                // Add custom formatter
68
                $container
69 1
                    ->getDefinition('httplug.collector.formatter')
70 1
                    ->replaceArgument(0, new Reference($config['profiling']['formatter']))
71
                ;
72
            }
73
74
            $container
75 12
                ->getDefinition('httplug.formatter.full_http_message')
76 12
                ->addArgument($config['profiling']['captured_body_length'])
77
            ;
78
        }
79
80 13
        $this->configureClients($container, $config);
81 13
        $this->configurePlugins($container, $config['plugins']); // must be after clients, as clients.X.plugins might use plugins as templates that will be removed
82 13
        $this->configureAutoDiscoveryClients($container, $config);
83 13
    }
84
85
    /**
86
     * Configure client services.
87
     *
88
     * @param ContainerBuilder $container
89
     * @param array            $config
90
     */
91 13
    private function configureClients(ContainerBuilder $container, array $config)
92
    {
93 13
        $first = null;
94 13
        $clients = [];
95
96 13
        foreach ($config['clients'] as $name => $arguments) {
97 6
            if (null === $first) {
98
                // Save the name of the first configured client.
99 6
                $first = $name;
100
            }
101
102 6
            $this->configureClient($container, $name, $arguments);
103 6
            $clients[] = $name;
104
        }
105
106
        // If we have clients configured
107 13
        if (null !== $first) {
108
            // If we do not have a client named 'default'
109 6
            if (!isset($config['clients']['default'])) {
110
                // Alias the first client to httplug.client.default
111 6
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
112
            }
113
        }
114 13
    }
115
116
    /**
117
     * Configure all Httplug plugins or remove their service definition.
118
     *
119
     * @param ContainerBuilder $container
120
     * @param array            $config
121
     */
122 13
    private function configurePlugins(ContainerBuilder $container, array $config)
123
    {
124 13
        if (!empty($config['authentication'])) {
125
            $this->configureAuthentication($container, $config['authentication']);
126
        }
127 13
        unset($config['authentication']);
128
129 13
        foreach ($config as $name => $pluginConfig) {
130 13
            $pluginId = 'httplug.plugin.'.$name;
131
132 13
            if ($this->isConfigEnabled($container, $pluginConfig)) {
133 13
                $def = $container->getDefinition($pluginId);
134 13
                $this->configurePluginByName($name, $def, $pluginConfig, $container, $pluginId);
135
            }
136
        }
137 13
    }
138
139
    /**
140
     * @param string           $name
141
     * @param Definition       $definition
142
     * @param array            $config
143
     * @param ContainerBuilder $container  In case we need to add additional services for this plugin
144
     * @param string           $serviceId  Service id of the plugin, in case we need to add additional services for this plugin.
145
     */
146 13
    private function configurePluginByName($name, Definition $definition, array $config, ContainerBuilder $container, $serviceId)
147
    {
148 13
        switch ($name) {
149 13
            case 'cache':
150 2
                $options = $config['config'];
151 2
                if (!empty($options['cache_key_generator'])) {
152 1
                    $options['cache_key_generator'] = new Reference($options['cache_key_generator']);
153
                }
154
155
                $definition
156 2
                    ->replaceArgument(0, new Reference($config['cache_pool']))
157 2
                    ->replaceArgument(1, new Reference($config['stream_factory']))
158 2
                    ->replaceArgument(2, $options);
159
160 2
                break;
161 13
            case 'cookie':
162
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
163
164
                break;
165 13
            case 'decoder':
166 13
                $definition->addArgument([
167 13
                    'use_content_encoding' => $config['use_content_encoding'],
168
                ]);
169
170 13
                break;
171 13
            case 'history':
172
                $definition->replaceArgument(0, new Reference($config['journal']));
173
174
                break;
175 13
            case 'logger':
176 13
                $definition->replaceArgument(0, new Reference($config['logger']));
177 13
                if (!empty($config['formatter'])) {
178
                    $definition->replaceArgument(1, new Reference($config['formatter']));
179
                }
180
181 13
                break;
182 13
            case 'redirect':
183 13
                $definition->addArgument([
184 13
                    'preserve_header' => $config['preserve_header'],
185 13
                    'use_default_for_multiple' => $config['use_default_for_multiple'],
186
                ]);
187
188 13
                break;
189 13
            case 'retry':
190 13
                $definition->addArgument([
191 13
                    'retries' => $config['retry'],
192
                ]);
193
194 13
                break;
195 13
            case 'stopwatch':
196 13
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
197
198 13
                break;
199
200
            /* client specific plugins */
201
202 5 View Code Duplication
            case 'add_host':
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...
203 5
                $hostUriService = $serviceId.'.host_uri';
204 5
                $this->createUri($container, $hostUriService, $config['host']);
205 5
                $definition->replaceArgument(0, new Reference($hostUriService));
206 5
                $definition->replaceArgument(1, [
207 5
                    'replace' => $config['replace'],
208
                ]);
209
210 5
                break;
211 1
            case 'add_path':
212
                $pathUriService = $serviceId.'.path_uri';
213
                $this->createUri($container, $pathUriService, $config['path']);
214
                $definition->replaceArgument(0, new Reference($pathUriService));
215
216
                break;
217 1 View Code Duplication
            case 'base_uri':
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...
218
                $baseUriService = $serviceId.'.base_uri';
219
                $this->createUri($container, $baseUriService, $config['uri']);
220
                $definition->replaceArgument(0, new Reference($baseUriService));
221
                $definition->replaceArgument(1, [
222
                    'replace' => $config['replace'],
223
                ]);
224
225
                break;
226 1
            case 'header_append':
227 1
            case 'header_defaults':
228 1
            case 'header_set':
229 1
            case 'header_remove':
230 1
                $definition->replaceArgument(0, $config['headers']);
231
232 1
                break;
233
234
            default:
235
                throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name));
236
        }
237 13
    }
238
239
    /**
240
     * @param ContainerBuilder $container
241
     * @param array            $config
242
     *
243
     * @return array List of service ids for the authentication plugins.
244
     */
245 5
    private function configureAuthentication(ContainerBuilder $container, array $config, $servicePrefix = 'httplug.plugin.authentication')
246
    {
247 5
        $pluginServices = [];
248
249 5
        foreach ($config as $name => $values) {
250 5
            $authServiceKey = sprintf($servicePrefix.'.%s.auth', $name);
251 5
            switch ($values['type']) {
252 5
                case 'bearer':
253
                    $container->register($authServiceKey, Bearer::class)
254
                        ->addArgument($values['token']);
255
256
                    break;
257 5
                case 'basic':
258 5
                    $container->register($authServiceKey, BasicAuth::class)
259 5
                        ->addArgument($values['username'])
260 5
                        ->addArgument($values['password']);
261
262 5
                    break;
263
                case 'wsse':
264
                    $container->register($authServiceKey, Wsse::class)
265
                        ->addArgument($values['username'])
266
                        ->addArgument($values['password']);
267
268
                    break;
269
                case 'service':
270
                    $authServiceKey = $values['service'];
271
272
                    break;
273
                default:
274
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
275
            }
276
277 5
            $pluginServiceKey = $servicePrefix.'.'.$name;
278 5
            $container->register($pluginServiceKey, AuthenticationPlugin::class)
279 5
                ->addArgument(new Reference($authServiceKey))
280
            ;
281 5
            $pluginServices[] = $pluginServiceKey;
282
        }
283
284 5
        return $pluginServices;
285
    }
286
287
    /**
288
     * @param ContainerBuilder $container
289
     * @param string           $clientName
290
     * @param array            $arguments
291
     */
292 6
    private function configureClient(ContainerBuilder $container, $clientName, array $arguments)
293
    {
294 6
        $serviceId = 'httplug.client.'.$clientName;
295
296 6
        $plugins = [];
297 6
        foreach ($arguments['plugins'] as $plugin) {
298 6
            $pluginName = key($plugin);
299 6
            $pluginConfig = current($plugin);
300 6
            if ('reference' === $pluginName) {
301 6
                $plugins[] = $pluginConfig['id'];
302 5
            } elseif ('authentication' === $pluginName) {
303 5
                $plugins = array_merge($plugins, $this->configureAuthentication($container, $pluginConfig, $serviceId.'.authentication'));
304
            } else {
305 6
                $plugins[] = $this->configurePlugin($container, $serviceId, $pluginName, $pluginConfig);
306
            }
307
        }
308
309
        $container
310 6
            ->register($serviceId.'.client', HttpClient::class)
311 6
            ->setFactory([new Reference($arguments['factory']), 'createClient'])
312 6
            ->addArgument($arguments['config'])
313 6
            ->setPublic(false)
314
        ;
315
316
        $container
317 6
            ->register($serviceId, PluginClient::class)
318 6
            ->setFactory([new Reference(PluginClientFactory::class), 'createClient'])
319 6
            ->addArgument(new Reference($serviceId.'.client'))
320 6
            ->addArgument(
321 6
                array_map(
322 6
                    function ($id) {
323 6
                        return new Reference($id);
324 6
                    },
325 6
                    $plugins
326
                )
327
            )
328 6
            ->addArgument([
329 6
                'client_name' => $clientName,
330
            ])
331
        ;
332
333
        /*
334
         * Decorate the client with clients from client-common
335
         */
336 6 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...
337
            $container
338
                ->register($serviceId.'.flexible', FlexibleHttpClient::class)
339
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
340
                ->setPublic(false)
341
                ->setDecoratedService($serviceId)
342
            ;
343
        }
344
345 6 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...
346
            $container
347
                ->register($serviceId.'.http_methods', HttpMethodsClient::class)
348
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
349
                ->setPublic(false)
350
                ->setDecoratedService($serviceId)
351
            ;
352
        }
353
354 6 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...
355
            $container
356
                ->register($serviceId.'.batch_client', BatchClient::class)
357
                ->setArguments([new Reference($serviceId.'.batch_client.inner')])
358
                ->setPublic(false)
359
                ->setDecoratedService($serviceId)
360
            ;
361
        }
362 6
    }
363
364
    /**
365
     * Create a URI object with the default URI factory.
366
     *
367
     * @param ContainerBuilder $container
368
     * @param string           $serviceId Name of the private service to create
369
     * @param string           $uri       String representation of the URI
370
     */
371 5
    private function createUri(ContainerBuilder $container, $serviceId, $uri)
372
    {
373
        $container
374 5
            ->register($serviceId, UriInterface::class)
375 5
            ->setPublic(false)
376 5
            ->setFactory([new Reference('httplug.uri_factory'), 'createUri'])
377 5
            ->addArgument($uri)
378
        ;
379 5
    }
380
381
    /**
382
     * Make the user can select what client is used for auto discovery. If none is provided, a service will be created
383
     * by finding a client using auto discovery.
384
     *
385
     * @param ContainerBuilder $container
386
     * @param array            $config
387
     */
388 13
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config)
389
    {
390 13
        $httpClient = $config['discovery']['client'];
391 13 View Code Duplication
        if ('auto' !== $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...
392 2
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_client');
393 2
            $container->removeDefinition('httplug.collector.auto_discovered_client');
394
395 2
            if (!empty($httpClient)) {
396 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_client', $httpClient);
397 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_client')->setPublic(false);
398
            }
399
        }
400
401 13
        $asyncHttpClient = $config['discovery']['async_client'];
402 13 View Code Duplication
        if ('auto' !== $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...
403 11
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_async');
404 11
            $container->removeDefinition('httplug.collector.auto_discovered_async');
405
406 11
            if (!empty($asyncHttpClient)) {
407 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_async', $asyncHttpClient);
408 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_async')->setPublic(false);
409
            }
410
        }
411
412 13
        if (null === $httpClient && null === $asyncHttpClient) {
413 1
            $container->removeDefinition('httplug.strategy');
414
415 1
            return;
416
        }
417 12
    }
418
419
    /**
420
     * {@inheritdoc}
421
     */
422 13
    public function getConfiguration(array $config, ContainerBuilder $container)
423
    {
424 13
        return new Configuration($container->getParameter('kernel.debug'));
425
    }
426
427
    /**
428
     * Configure a plugin using the parent definition from plugins.xml.
429
     *
430
     * @param ContainerBuilder $container
431
     * @param string           $serviceId
432
     * @param string           $pluginName
433
     * @param array            $pluginConfig
434
     *
435
     * @return string configured service id
436
     */
437 5
    private function configurePlugin(ContainerBuilder $container, $serviceId, $pluginName, array $pluginConfig)
438
    {
439 5
        $pluginServiceId = $serviceId.'.plugin.'.$pluginName;
440
441 5
        $definition = class_exists(ChildDefinition::class)
442 5
            ? new ChildDefinition('httplug.plugin.'.$pluginName)
443 5
            : new DefinitionDecorator('httplug.plugin.'.$pluginName);
444
445 5
        $this->configurePluginByName($pluginName, $definition, $pluginConfig, $container, $pluginServiceId);
446 5
        $container->setDefinition($pluginServiceId, $definition);
447
448 5
        return $pluginServiceId;
449
    }
450
}
451