Completed
Push — master ( 542d39...667ffc )
by David
12:34
created

HttplugExtension::configurePluginByName()   C

Complexity

Conditions 16
Paths 16

Size

Total Lines 76
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 50
CRAP Score 16.9079

Importance

Changes 0
Metric Value
dl 0
loc 76
ccs 50
cts 59
cp 0.8475
rs 5.248
c 0
b 0
f 0
cc 16
eloc 53
nc 16
nop 5
crap 16.9079

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

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
425
426 5
        $this->configurePluginByName($pluginName, $definition, $pluginConfig, $container, $pluginServiceId);
427 5
        $container->setDefinition($pluginServiceId, $definition);
428
429 5
        return $pluginServiceId;
430
    }
431
}
432