Completed
Push — master ( 9c1ff7...5ea928 )
by David
04:44
created

HttplugExtension::configurePluginByName()   D

Complexity

Conditions 17
Paths 17

Size

Total Lines 86
Code Lines 60

Duplication

Lines 18
Ratio 20.93 %

Code Coverage

Tests 47
CRAP Score 19.4319

Importance

Changes 0
Metric Value
dl 18
loc 86
ccs 47
cts 59
cp 0.7966
rs 4.8361
c 0
b 0
f 0
cc 17
eloc 60
nc 17
nop 5
crap 19.4319

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