Completed
Push — master ( 5c4db9...c91eef )
by Tobias
11:45
created

HttplugExtension::configureClient()   C

Complexity

Conditions 8
Paths 64

Size

Total Lines 75
Code Lines 50

Duplication

Lines 20
Ratio 26.67 %

Code Coverage

Tests 32
CRAP Score 9.2978

Importance

Changes 0
Metric Value
dl 20
loc 75
ccs 32
cts 44
cp 0.7272
rs 6.2413
c 0
b 0
f 0
cc 8
eloc 50
nc 64
nop 3
crap 9.2978

How to fix   Long Method   

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 14
    public function load(array $configs, ContainerBuilder $container)
37
    {
38 14
        $configuration = $this->getConfiguration($configs, $container);
39 14
        $config = $this->processConfiguration($configuration, $configs);
40
41 14
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
42
43 14
        $loader->load('services.xml');
44 14
        $loader->load('plugins.xml');
45 14
        if (\class_exists(MockClient::class)) {
46 14
            $loader->load('mock-client.xml');
47
        }
48
49
        // Register default services
50 14
        foreach ($config['classes'] as $service => $class) {
51 14
            if (!empty($class)) {
52 14
                $container->register(sprintf('httplug.%s.default', $service), $class);
53
            }
54
        }
55
56
        // Set main aliases
57 14
        foreach ($config['main_alias'] as $type => $id) {
58 14
            $container->setAlias(sprintf('httplug.%s', $type), new Alias($id, true));
59
        }
60
61
        // Configure toolbar
62 14
        $profilingEnabled = $this->isConfigEnabled($container, $config['profiling']);
63 14
        if ($profilingEnabled) {
64 13
            $loader->load('data-collector.xml');
65
66 13
            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 13
                ->getDefinition('httplug.formatter.full_http_message')
76 13
                ->addArgument($config['profiling']['captured_body_length'])
77
            ;
78
        }
79
80 14
        $this->configureClients($container, $config);
81 14
        $this->configurePlugins($container, $config['plugins']); // must be after clients, as clients.X.plugins might use plugins as templates that will be removed
82 14
        $this->configureAutoDiscoveryClients($container, $config);
83 14
    }
84
85
    /**
86
     * Configure client services.
87
     *
88
     * @param ContainerBuilder $container
89
     * @param array            $config
90
     */
91 14
    private function configureClients(ContainerBuilder $container, array $config)
92
    {
93 14
        $first = null;
94 14
        $clients = [];
95
96 14
        foreach ($config['clients'] as $name => $arguments) {
97 7
            if (null === $first) {
98
                // Save the name of the first configured client.
99 7
                $first = $name;
100
            }
101
102 7
            $this->configureClient($container, $name, $arguments);
103 7
            $clients[] = $name;
104
        }
105
106
        // If we have clients configured
107 14
        if (null !== $first) {
108
            // If we do not have a client named 'default'
109 7
            if (!isset($config['clients']['default'])) {
110
                // Alias the first client to httplug.client.default
111 7
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
112
            }
113
        }
114 14
    }
115
116
    /**
117
     * Configure all Httplug plugins or remove their service definition.
118
     *
119
     * @param ContainerBuilder $container
120
     * @param array            $config
121
     */
122 14
    private function configurePlugins(ContainerBuilder $container, array $config)
123
    {
124 14
        if (!empty($config['authentication'])) {
125
            $this->configureAuthentication($container, $config['authentication']);
126
        }
127 14
        unset($config['authentication']);
128
129 14
        foreach ($config as $name => $pluginConfig) {
130 14
            $pluginId = 'httplug.plugin.'.$name;
131
132 14
            if ($this->isConfigEnabled($container, $pluginConfig)) {
133 14
                $def = $container->getDefinition($pluginId);
134 14
                $this->configurePluginByName($name, $def, $pluginConfig, $container, $pluginId);
135
            }
136
        }
137 14
    }
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 14
    private function configurePluginByName($name, Definition $definition, array $config, ContainerBuilder $container, $serviceId)
147
    {
148 14
        switch ($name) {
149 14
            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 14
            case 'cookie':
162
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
163
164
                break;
165 14
            case 'decoder':
166 14
                $definition->addArgument([
167 14
                    'use_content_encoding' => $config['use_content_encoding'],
168
                ]);
169
170 14
                break;
171 14
            case 'history':
172
                $definition->replaceArgument(0, new Reference($config['journal']));
173
174
                break;
175 14
            case 'logger':
176 14
                $definition->replaceArgument(0, new Reference($config['logger']));
177 14
                if (!empty($config['formatter'])) {
178
                    $definition->replaceArgument(1, new Reference($config['formatter']));
179
                }
180
181 14
                break;
182 14
            case 'redirect':
183 14
                $definition->addArgument([
184 14
                    'preserve_header' => $config['preserve_header'],
185 14
                    'use_default_for_multiple' => $config['use_default_for_multiple'],
186
                ]);
187
188 14
                break;
189 14
            case 'retry':
190 14
                $definition->addArgument([
191 14
                    'retries' => $config['retry'],
192
                ]);
193
194 14
                break;
195 14
            case 'stopwatch':
196 14
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
197
198 14
                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 14
    }
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 7
    private function configureClient(ContainerBuilder $container, $clientName, array $arguments)
293
    {
294 7
        $serviceId = 'httplug.client.'.$clientName;
295
296 7
        $plugins = [];
297 7
        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 7
        if (empty($arguments['service'])) {
310
            $container
311 6
                ->register($serviceId.'.client', HttpClient::class)
312 6
                ->setFactory([new Reference($arguments['factory']), 'createClient'])
313 6
                ->addArgument($arguments['config'])
314 6
                ->setPublic(false);
315
        } else {
316
            $container
317 1
                ->setAlias($serviceId.'.client', new Alias($arguments['service'], false));
318
        }
319
320
        $container
321 7
            ->register($serviceId, PluginClient::class)
322 7
            ->setFactory([new Reference(PluginClientFactory::class), 'createClient'])
323 7
            ->addArgument(new Reference($serviceId.'.client'))
324 7
            ->addArgument(
325 7
                array_map(
326 7
                    function ($id) {
327 6
                        return new Reference($id);
328 7
                    },
329 7
                    $plugins
330
                )
331
            )
332 7
            ->addArgument([
333 7
                'client_name' => $clientName,
334
            ])
335
        ;
336
337
        /*
338
         * Decorate the client with clients from client-common
339
         */
340 7 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...
341
            $container
342
                ->register($serviceId.'.flexible', FlexibleHttpClient::class)
343
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
344
                ->setPublic(false)
345
                ->setDecoratedService($serviceId)
346
            ;
347
        }
348
349 7 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...
350
            $container
351
                ->register($serviceId.'.http_methods', HttpMethodsClient::class)
352
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
353
                ->setPublic(false)
354
                ->setDecoratedService($serviceId)
355
            ;
356
        }
357
358 7 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...
359
            $container
360
                ->register($serviceId.'.batch_client', BatchClient::class)
361
                ->setArguments([new Reference($serviceId.'.batch_client.inner')])
362
                ->setPublic(false)
363
                ->setDecoratedService($serviceId)
364
            ;
365
        }
366 7
    }
367
368
    /**
369
     * Create a URI object with the default URI factory.
370
     *
371
     * @param ContainerBuilder $container
372
     * @param string           $serviceId Name of the private service to create
373
     * @param string           $uri       String representation of the URI
374
     */
375 5
    private function createUri(ContainerBuilder $container, $serviceId, $uri)
376
    {
377
        $container
378 5
            ->register($serviceId, UriInterface::class)
379 5
            ->setPublic(false)
380 5
            ->setFactory([new Reference('httplug.uri_factory'), 'createUri'])
381 5
            ->addArgument($uri)
382
        ;
383 5
    }
384
385
    /**
386
     * Make the user can select what client is used for auto discovery. If none is provided, a service will be created
387
     * by finding a client using auto discovery.
388
     *
389
     * @param ContainerBuilder $container
390
     * @param array            $config
391
     */
392 14
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config)
393
    {
394 14
        $httpClient = $config['discovery']['client'];
395 14 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...
396 2
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_client');
397 2
            $container->removeDefinition('httplug.collector.auto_discovered_client');
398
399 2
            if (!empty($httpClient)) {
400 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_client', $httpClient);
401 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_client')->setPublic(false);
402
            }
403
        }
404
405 14
        $asyncHttpClient = $config['discovery']['async_client'];
406 14 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...
407 12
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_async');
408 12
            $container->removeDefinition('httplug.collector.auto_discovered_async');
409
410 12
            if (!empty($asyncHttpClient)) {
411 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_async', $asyncHttpClient);
412 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_async')->setPublic(false);
413
            }
414
        }
415
416 14
        if (null === $httpClient && null === $asyncHttpClient) {
417 1
            $container->removeDefinition('httplug.strategy');
418
419 1
            return;
420
        }
421 13
    }
422
423
    /**
424
     * {@inheritdoc}
425
     */
426 14
    public function getConfiguration(array $config, ContainerBuilder $container)
427
    {
428 14
        return new Configuration($container->getParameter('kernel.debug'));
429
    }
430
431
    /**
432
     * Configure a plugin using the parent definition from plugins.xml.
433
     *
434
     * @param ContainerBuilder $container
435
     * @param string           $serviceId
436
     * @param string           $pluginName
437
     * @param array            $pluginConfig
438
     *
439
     * @return string configured service id
440
     */
441 5
    private function configurePlugin(ContainerBuilder $container, $serviceId, $pluginName, array $pluginConfig)
442
    {
443 5
        $pluginServiceId = $serviceId.'.plugin.'.$pluginName;
444
445 5
        $definition = class_exists(ChildDefinition::class)
446 5
            ? new ChildDefinition('httplug.plugin.'.$pluginName)
447 5
            : new DefinitionDecorator('httplug.plugin.'.$pluginName);
448
449 5
        $this->configurePluginByName($pluginName, $definition, $pluginConfig, $container, $pluginServiceId);
450 5
        $container->setDefinition($pluginServiceId, $definition);
451
452 5
        return $pluginServiceId;
453
    }
454
}
455