Completed
Push — master ( c88cc4...bbe830 )
by David
15:29 queued 11s
created

HttplugExtension::load()   C

Complexity

Conditions 10
Paths 120

Size

Total Lines 57

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 10.0036

Importance

Changes 0
Metric Value
dl 0
loc 57
ccs 29
cts 30
cp 0.9667
rs 6.9381
c 0
b 0
f 0
cc 10
nc 120
nop 2
crap 10.0036

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\BatchClientInterface;
7
use Http\Client\Common\FlexibleHttpClient;
8
use Http\Client\Common\HttpMethodsClient;
9
use Http\Client\Common\HttpMethodsClientInterface;
10
use Http\Client\Common\Plugin\AuthenticationPlugin;
11
use Http\Client\Common\PluginClient;
12
use Http\Client\Common\PluginClientFactory;
13
use Http\Client\HttpAsyncClient;
14
use Http\Client\HttpClient;
15
use Http\Message\Authentication\BasicAuth;
16
use Http\Message\Authentication\Bearer;
17
use Http\Message\Authentication\QueryParam;
18
use Http\Message\Authentication\Wsse;
19
use Http\Mock\Client as MockClient;
20
use Psr\Http\Message\UriInterface;
21
use Symfony\Component\Config\FileLocator;
22
use Symfony\Component\DependencyInjection\Alias;
23
use Symfony\Component\DependencyInjection\ChildDefinition;
24
use Symfony\Component\DependencyInjection\ContainerBuilder;
25
use Symfony\Component\DependencyInjection\Definition;
26
use Symfony\Component\DependencyInjection\DefinitionDecorator;
27
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
28
use Symfony\Component\DependencyInjection\Reference;
29
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
30
use Twig\Environment as TwigEnvironment;
31
32
/**
33
 * @author David Buchmann <[email protected]>
34
 * @author Tobias Nyholm <[email protected]>
35
 */
36
class HttplugExtension extends Extension
37
{
38
    /**
39
     * {@inheritdoc}
40
     */
41 28
    public function load(array $configs, ContainerBuilder $container)
42
    {
43 28
        $configuration = $this->getConfiguration($configs, $container);
44 28
        $config = $this->processConfiguration($configuration, $configs);
45
46 28
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
47
48 28
        $loader->load('services.xml');
49 28
        $loader->load('plugins.xml');
50 28
        if (\class_exists(MockClient::class)) {
51 28
            $loader->load('mock-client.xml');
52
        }
53
54
        // Register default services
55 28
        foreach ($config['classes'] as $service => $class) {
56 28
            if (!empty($class)) {
57 1
                $container->register(sprintf('httplug.%s.default', $service), $class);
58
            }
59
        }
60
61
        // Set main aliases
62 28
        foreach ($config['main_alias'] as $type => $id) {
63 28
            $container->setAlias(sprintf('httplug.%s', $type), new Alias($id, true));
64
        }
65
66
        // Configure toolbar
67 28
        $profilingEnabled = $this->isConfigEnabled($container, $config['profiling']);
68 28
        if ($profilingEnabled) {
69 25
            $loader->load('data-collector.xml');
70
71 25
            if (!empty($config['profiling']['formatter'])) {
72
                // Add custom formatter
73
                $container
74 1
                    ->getDefinition('httplug.collector.formatter')
75 1
                    ->replaceArgument(0, new Reference($config['profiling']['formatter']))
76
                ;
77
            }
78
79
            $container
80 25
                ->getDefinition('httplug.formatter.full_http_message')
81 25
                ->addArgument($config['profiling']['captured_body_length'])
82
            ;
83
84 25
            if (!class_exists(TwigEnvironment::class) && !class_exists(\Twig_Environment::class)) {
85
                $container->removeDefinition('httplug.collector.twig.http_message');
86
            }
87
        }
88
89 28
        $this->configureClients($container, $config);
90 28
        $this->configurePlugins($container, $config['plugins']); // must be after clients, as clients.X.plugins might use plugins as templates that will be removed
91 28
        $this->configureAutoDiscoveryClients($container, $config);
92
93 28
        if (!$config['default_client_autowiring']) {
94 1
            $container->removeAlias(HttpAsyncClient::class);
95 1
            $container->removeAlias(HttpClient::class);
96
        }
97 28
    }
98
99
    /**
100
     * Configure client services.
101
     *
102
     * @param ContainerBuilder $container
103
     * @param array            $config
104
     */
105 28
    private function configureClients(ContainerBuilder $container, array $config)
106
    {
107 28
        $first = null;
108 28
        $clients = [];
109
110 28
        foreach ($config['clients'] as $name => $arguments) {
111 18
            if (null === $first) {
112
                // Save the name of the first configured client.
113 18
                $first = $name;
114
            }
115
116 18
            $this->configureClient($container, $name, $arguments);
117 18
            $clients[] = $name;
118
        }
119
120
        // If we have clients configured
121 28
        if (null !== $first) {
122
            // If we do not have a client named 'default'
123 18
            if (!array_key_exists('default', $config['clients'])) {
124 18
                $serviceId = 'httplug.client.'.$first;
125
                // Alias the first client to httplug.client.default
126 18
                $container->setAlias('httplug.client.default', $serviceId);
127 18
                $default = $first;
128
            } else {
129
                $default = 'default';
130
            }
131
132
            // Autowiring alias for special clients, if they are enabled on the default client
133 18
            if ($config['clients'][$default]['flexible_client']) {
134 2
                $container->setAlias(FlexibleHttpClient::class, $serviceId.'.flexible');
0 ignored issues
show
Bug introduced by
The variable $serviceId does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
135
            }
136 18
            if ($config['clients'][$default]['http_methods_client']) {
137 2
                if (\interface_exists(HttpMethodsClientInterface::class)) {
138
                    // support for client-common 1.9
139 2
                    $container->setAlias(HttpMethodsClientInterface::class, $serviceId.'.http_methods');
140
                }
141
            }
142 18
            if ($config['clients'][$default]['batch_client']) {
143 2
                if (\interface_exists(BatchClientInterface::class)) {
144
                    // support for client-common 1.9
145 2
                    $container->setAlias(BatchClientInterface::class, $serviceId.'.batch_client');
146
                }
147
            }
148
        }
149 28
    }
150
151
    /**
152
     * Configure all Httplug plugins or remove their service definition.
153
     *
154
     * @param ContainerBuilder $container
155
     * @param array            $config
156
     */
157 28
    private function configurePlugins(ContainerBuilder $container, array $config)
158
    {
159 28
        if (!empty($config['authentication'])) {
160
            $this->configureAuthentication($container, $config['authentication']);
161
        }
162 28
        unset($config['authentication']);
163
164 28
        foreach ($config as $name => $pluginConfig) {
165 28
            $pluginId = 'httplug.plugin.'.$name;
166
167 28
            if ($this->isConfigEnabled($container, $pluginConfig)) {
168 28
                $def = $container->getDefinition($pluginId);
169 28
                $this->configurePluginByName($name, $def, $pluginConfig, $container, $pluginId);
170
            }
171
        }
172 28
    }
173
174
    /**
175
     * @param string           $name
176
     * @param Definition       $definition
177
     * @param array            $config
178
     * @param ContainerBuilder $container  In case we need to add additional services for this plugin
179
     * @param string           $serviceId  service id of the plugin, in case we need to add additional services for this plugin
180
     */
181 28
    private function configurePluginByName($name, Definition $definition, array $config, ContainerBuilder $container, $serviceId)
182
    {
183 28
        switch ($name) {
184 28
            case 'cache':
185 2
                $options = $config['config'];
186 2
                if (!empty($options['cache_key_generator'])) {
187 1
                    $options['cache_key_generator'] = new Reference($options['cache_key_generator']);
188
                }
189
190
                $definition
191 2
                    ->replaceArgument(0, new Reference($config['cache_pool']))
192 2
                    ->replaceArgument(1, new Reference($config['stream_factory']))
193 2
                    ->replaceArgument(2, $options);
194
195 2
                break;
196
197 28
            case 'cookie':
198
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
199
200
                break;
201
202 28
            case 'decoder':
203 28
                $definition->addArgument([
204 28
                    'use_content_encoding' => $config['use_content_encoding'],
205
                ]);
206
207 28
                break;
208
209 28
            case 'history':
210
                $definition->replaceArgument(0, new Reference($config['journal']));
211
212
                break;
213
214 28
            case 'logger':
215 28
                $definition->replaceArgument(0, new Reference($config['logger']));
216 28
                if (!empty($config['formatter'])) {
217
                    $definition->replaceArgument(1, new Reference($config['formatter']));
218
                }
219
220 28
                break;
221
222 28
            case 'redirect':
223 28
                $definition->addArgument([
224 28
                    'preserve_header' => $config['preserve_header'],
225 28
                    'use_default_for_multiple' => $config['use_default_for_multiple'],
226
                ]);
227
228 28
                break;
229
230 28
            case 'retry':
231 28
                $definition->addArgument([
232 28
                    'retries' => $config['retry'],
233
                ]);
234
235 28
                break;
236
237 28
            case 'stopwatch':
238 28
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
239
240 28
                break;
241
242
            /* client specific plugins */
243
244 6 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...
245 6
                $hostUriService = $serviceId.'.host_uri';
246 6
                $this->createUri($container, $hostUriService, $config['host']);
247 6
                $definition->replaceArgument(0, new Reference($hostUriService));
248 6
                $definition->replaceArgument(1, [
249 6
                    'replace' => $config['replace'],
250
                ]);
251
252 6
                break;
253
254 1
            case 'add_path':
255
                $pathUriService = $serviceId.'.path_uri';
256
                $this->createUri($container, $pathUriService, $config['path']);
257
                $definition->replaceArgument(0, new Reference($pathUriService));
258
259
                break;
260
261 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...
262
                $baseUriService = $serviceId.'.base_uri';
263
                $this->createUri($container, $baseUriService, $config['uri']);
264
                $definition->replaceArgument(0, new Reference($baseUriService));
265
                $definition->replaceArgument(1, [
266
                    'replace' => $config['replace'],
267
                ]);
268
269
                break;
270
271 1
            case 'content_type':
272 1
                $definition->replaceArgument(0, $config);
273 1
                break;
274
275 1
            case 'header_append':
276 1
            case 'header_defaults':
277 1
            case 'header_set':
278 1
            case 'header_remove':
279 1
                $definition->replaceArgument(0, $config['headers']);
280
281 1
                break;
282
283 1
            case 'query_defaults':
284 1
                $definition->replaceArgument(0, $config['parameters']);
285
286 1
                break;
287
288
            default:
289
                throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name));
290
        }
291 28
    }
292
293
    /**
294
     * @param ContainerBuilder $container
295
     * @param array            $config
296
     *
297
     * @return array list of service ids for the authentication plugins
298
     */
299 6
    private function configureAuthentication(ContainerBuilder $container, array $config, $servicePrefix = 'httplug.plugin.authentication')
300
    {
301 6
        $pluginServices = [];
302
303 6
        foreach ($config as $name => $values) {
304 6
            $authServiceKey = sprintf($servicePrefix.'.%s.auth', $name);
305 6
            switch ($values['type']) {
306 6
                case 'bearer':
307
                    $container->register($authServiceKey, Bearer::class)
308
                        ->addArgument($values['token']);
309
310
                    break;
311 6
                case 'basic':
312 6
                    $container->register($authServiceKey, BasicAuth::class)
313 6
                        ->addArgument($values['username'])
314 6
                        ->addArgument($values['password']);
315
316 6
                    break;
317
                case 'wsse':
318
                    $container->register($authServiceKey, Wsse::class)
319
                        ->addArgument($values['username'])
320
                        ->addArgument($values['password']);
321
322
                    break;
323
                case 'query_param':
324
                    $container->register($authServiceKey, QueryParam::class)
325
                        ->addArgument($values['params']);
326
327
                    break;
328
                case 'service':
329
                    $authServiceKey = $values['service'];
330
331
                    break;
332
                default:
333
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
334
            }
335
336 6
            $pluginServiceKey = $servicePrefix.'.'.$name;
337 6
            $container->register($pluginServiceKey, AuthenticationPlugin::class)
338 6
                ->addArgument(new Reference($authServiceKey))
339
            ;
340 6
            $pluginServices[] = $pluginServiceKey;
341
        }
342
343 6
        return $pluginServices;
344
    }
345
346
    /**
347
     * @param ContainerBuilder $container
348
     * @param string           $clientName
349
     * @param array            $arguments
350
     */
351 18
    private function configureClient(ContainerBuilder $container, $clientName, array $arguments)
352
    {
353 18
        $serviceId = 'httplug.client.'.$clientName;
354
355 18
        $plugins = [];
356 18
        foreach ($arguments['plugins'] as $plugin) {
357 9
            $pluginName = key($plugin);
358 9
            $pluginConfig = current($plugin);
359 9
            if ('reference' === $pluginName) {
360 9
                $plugins[] = $pluginConfig['id'];
361 6
            } elseif ('authentication' === $pluginName) {
362 6
                $plugins = array_merge($plugins, $this->configureAuthentication($container, $pluginConfig, $serviceId.'.authentication'));
363
            } else {
364 6
                $plugins[] = $this->configurePlugin($container, $serviceId, $pluginName, $pluginConfig);
365
            }
366
        }
367
368 18
        if (empty($arguments['service'])) {
369
            $container
370 17
                ->register($serviceId.'.client', HttpClient::class)
371 17
                ->setFactory([new Reference($arguments['factory']), 'createClient'])
372 17
                ->addArgument($arguments['config'])
373 17
                ->setPublic(false);
374
        } else {
375
            $container
376 2
                ->setAlias($serviceId.'.client', new Alias($arguments['service'], false));
377
        }
378
379
        $definition = $container
380 18
            ->register($serviceId, PluginClient::class)
381 18
            ->setFactory([new Reference(PluginClientFactory::class), 'createClient'])
382 18
            ->addArgument(new Reference($serviceId.'.client'))
383 18
            ->addArgument(
384 18
                array_map(
385 18
                    function ($id) {
386 9
                        return new Reference($id);
387 18
                    },
388
                    $plugins
389
                )
390
            )
391 18
            ->addArgument([
392 18
                'client_name' => $clientName,
393
            ])
394
        ;
395
396 18
        if (is_bool($arguments['public'])) {
397 5
            $definition->setPublic($arguments['public']);
398
        }
399
400
        /*
401
         * Decorate the client with clients from client-common
402
         */
403 18 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...
404
            $container
405 2
                ->register($serviceId.'.flexible', FlexibleHttpClient::class)
406 2
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
407 2
                ->setPublic($arguments['public'] ? true : false)
408 2
                ->setDecoratedService($serviceId)
409
            ;
410
        }
411
412 18 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...
413
            $container
414 2
                ->register($serviceId.'.http_methods', HttpMethodsClient::class)
415 2
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
416 2
                ->setPublic($arguments['public'] ? true : false)
417 2
                ->setDecoratedService($serviceId)
418
            ;
419
        }
420
421 18 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...
422
            $container
423 2
                ->register($serviceId.'.batch_client', BatchClient::class)
424 2
                ->setArguments([new Reference($serviceId.'.batch_client.inner')])
425 2
                ->setPublic($arguments['public'] ? true : false)
426 2
                ->setDecoratedService($serviceId)
427
            ;
428
        }
429 18
    }
430
431
    /**
432
     * Create a URI object with the default URI factory.
433
     *
434
     * @param ContainerBuilder $container
435
     * @param string           $serviceId Name of the private service to create
436
     * @param string           $uri       String representation of the URI
437
     */
438 6
    private function createUri(ContainerBuilder $container, $serviceId, $uri)
439
    {
440
        $container
441 6
            ->register($serviceId, UriInterface::class)
442 6
            ->setPublic(false)
443 6
            ->setFactory([new Reference('httplug.uri_factory'), 'createUri'])
444 6
            ->addArgument($uri)
445
        ;
446 6
    }
447
448
    /**
449
     * Make the user can select what client is used for auto discovery. If none is provided, a service will be created
450
     * by finding a client using auto discovery.
451
     *
452
     * @param ContainerBuilder $container
453
     * @param array            $config
454
     */
455 28
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config)
456
    {
457 28
        $httpClient = $config['discovery']['client'];
458 28 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...
459 2
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_client');
460 2
            $container->removeDefinition('httplug.collector.auto_discovered_client');
461
462 2
            if (!empty($httpClient)) {
463 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_client', $httpClient);
464 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_client')->setPublic(false);
465
            }
466
        }
467
468 28
        $asyncHttpClient = $config['discovery']['async_client'];
469 28 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...
470 25
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_async');
471 25
            $container->removeDefinition('httplug.collector.auto_discovered_async');
472
473 25
            if (!empty($asyncHttpClient)) {
474 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_async', $asyncHttpClient);
475 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_async')->setPublic(false);
476
            }
477
        }
478
479 28
        if (null === $httpClient && null === $asyncHttpClient) {
480 1
            $container->removeDefinition('httplug.strategy');
481
482 1
            return;
483
        }
484 27
    }
485
486
    /**
487
     * {@inheritdoc}
488
     */
489 28
    public function getConfiguration(array $config, ContainerBuilder $container)
490
    {
491 28
        return new Configuration($container->getParameter('kernel.debug'));
492
    }
493
494
    /**
495
     * Configure a plugin using the parent definition from plugins.xml.
496
     *
497
     * @param ContainerBuilder $container
498
     * @param string           $serviceId
499
     * @param string           $pluginName
500
     * @param array            $pluginConfig
501
     *
502
     * @return string configured service id
503
     */
504 6
    private function configurePlugin(ContainerBuilder $container, $serviceId, $pluginName, array $pluginConfig)
505
    {
506 6
        $pluginServiceId = $serviceId.'.plugin.'.$pluginName;
507
508 6
        $definition = class_exists(ChildDefinition::class)
509 6
            ? new ChildDefinition('httplug.plugin.'.$pluginName)
510 6
            : new DefinitionDecorator('httplug.plugin.'.$pluginName);
511
512 6
        $this->configurePluginByName($pluginName, $definition, $pluginConfig, $container, $pluginServiceId);
513 6
        $container->setDefinition($pluginServiceId, $definition);
514
515 6
        return $pluginServiceId;
516
    }
517
}
518