Completed
Pull Request — master (#111)
by David
06:05
created

HttplugExtension::configureClient()   C

Complexity

Conditions 12
Paths 48

Size

Total Lines 107
Code Lines 67

Duplication

Lines 47
Ratio 43.93 %

Code Coverage

Tests 0
CRAP Score 156

Importance

Changes 7
Bugs 1 Features 5
Metric Value
c 7
b 1
f 5
dl 47
loc 107
ccs 0
cts 0
cp 0
rs 5.034
cc 12
eloc 67
nc 48
nop 4
crap 156

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\AddHostPlugin;
9
use Http\Client\Common\Plugin\AuthenticationPlugin;
10
use Http\Discovery\HttpAsyncClientDiscovery;
11
use Http\Discovery\HttpClientDiscovery;
12
use Http\HttplugBundle\ClientFactory\DummyClient;
13
use Http\HttplugBundle\ClientFactory\PluginClientFactory;
14
use Http\HttplugBundle\Collector\DebugPlugin;
15
use Http\Message\Authentication\BasicAuth;
16
use Http\Message\Authentication\Bearer;
17
use Http\Message\Authentication\Wsse;
18
use Psr\Http\Message\UriInterface;
19
use Symfony\Component\Config\FileLocator;
20
use Symfony\Component\DependencyInjection\ContainerBuilder;
21
use Symfony\Component\DependencyInjection\Definition;
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 8
     * {@inheritdoc}
34
     */
35 8
    public function load(array $configs, ContainerBuilder $container)
36 8
    {
37
        $configuration = $this->getConfiguration($configs, $container);
38 8
        $config = $this->processConfiguration($configuration, $configs);
1 ignored issue
show
Bug introduced by
It seems like $configuration defined by $this->getConfiguration($configs, $container) on line 37 can be null; however, Symfony\Component\Depend...:processConfiguration() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
39
40 8
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
41 8
42
        $loader->load('services.xml');
43
        $loader->load('plugins.xml');
44 8
45 8
        // Register default services
46 1
        foreach ($config['classes'] as $service => $class) {
47 1
            if (!empty($class)) {
48 8
                $container->register(sprintf('httplug.%s.default', $service), $class);
49
            }
50
        }
51 8
52 8
        // Set main aliases
53 8
        foreach ($config['main_alias'] as $type => $id) {
54
            $container->setAlias(sprintf('httplug.%s', $type), $id);
55
        }
56 8
57 5
        // Configure toolbar
58
        if ($config['profiling']['enabled']) {
59 5
            $loader->load('data-collector.xml');
60
61
            if (!empty($config['profiling']['formatter'])) {
62
                // Add custom formatter
63
                $container
64
                    ->getDefinition('httplug.collector.debug_collector')
65
                    ->replaceArgument(0, new Reference($config['profiling']['formatter']))
66
                ;
67
            }
68 5
69 5
            $container
70
                ->getDefinition('httplug.formatter.full_http_message')
71 5
                ->addArgument($config['profiling']['captured_body_length'])
72
            ;
73 8
        }
74 8
75 8
        $this->configurePlugins($container, $config['plugins']);
76 8
        $this->configureClients($container, $config);
77
        $this->configureAutoDiscoveryClients($container, $config);
78
    }
79
80
    /**
81
     * Configure client services.
82
     *
83
     * @param ContainerBuilder $container
84 8
     * @param array            $config
85
     */
86 8
    private function configureClients(ContainerBuilder $container, array $config)
87
    {
88 8
        $first = null;
89 3
90
        foreach ($config['clients'] as $name => $arguments) {
91 3
            if ($first === null) {
92 3
                // Save the name of the first configurated client.
93
                $first = $name;
94 3
            }
95 8
96
            $this->configureClient($container, $name, $arguments, $config['profiling']['enabled']);
97
        }
98 8
99
        // If we have clients configured
100 3
        if ($first !== null) {
101
            // If we do not have a client named 'default'
102 3
            if (!isset($config['clients']['default'])) {
103 3
                // Alias the first client to httplug.client.default
104 3
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
105 8
            }
106
        }
107
    }
108
109
    /**
110
     * @param ContainerBuilder $container
111 8
     * @param array            $config
112
     */
113 8
    private function configurePlugins(ContainerBuilder $container, array $config)
114
    {
115
        if (!empty($config['authentication'])) {
116 8
            $this->configureAuthentication($container, $config['authentication']);
117
        }
118 8
        unset($config['authentication']);
119 8
120
        foreach ($config as $name => $pluginConfig) {
121 8
            $pluginId = 'httplug.plugin.'.$name;
122 8
123 8
            if ($pluginConfig['enabled']) {
124 8
                $def = $container->getDefinition($pluginId);
125 8
                $this->configurePluginByName($name, $def, $pluginConfig);
126
            } else {
127 8
                $container->removeDefinition($pluginId);
128 8
            }
129
        }
130
    }
131
132
    /**
133
     * @param string     $name
134
     * @param Definition $definition
135 8
     * @param array      $config
136
     */
137
    private function configurePluginByName($name, Definition $definition, array $config)
138 8
    {
139
        switch ($name) {
140
            case 'cache':
141
                $definition
142
                    ->replaceArgument(0, new Reference($config['cache_pool']))
143
                    ->replaceArgument(1, new Reference($config['stream_factory']))
144 8
                    ->replaceArgument(2, $config['config']);
145
                break;
146
            case 'cookie':
147 8
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
148 8
                break;
149 8
            case 'decoder':
150 8
                $definition->addArgument($config['use_content_encoding']);
151
                break;
152
            case 'history':
153 8
                $definition->replaceArgument(0, new Reference($config['journal']));
154 8
                break;
155 8
            case 'logger':
156
                $definition->replaceArgument(0, new Reference($config['logger']));
157
                if (!empty($config['formatter'])) {
158 8
                    $definition->replaceArgument(1, new Reference($config['formatter']));
159 8
                }
160
                break;
161 8
            case 'redirect':
162 8
                $definition
163 8
                    ->addArgument($config['preserve_header'])
164 8
                    ->addArgument($config['use_default_for_multiple']);
165 8
                break;
166 8
            case 'retry':
167 8
                $definition->addArgument($config['retry']);
168 8
                break;
169 8
            case 'stopwatch':
170
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
171 8
                break;
172
        }
173
    }
174
175
    /**
176
     * @param ContainerBuilder $container
177
     * @param array            $config
178
     */
179
    private function configureAuthentication(ContainerBuilder $container, array $config)
180
    {
181
        foreach ($config as $name => $values) {
182
            $authServiceKey = sprintf('httplug.plugin.authentication.%s.auth', $name);
183
            switch ($values['type']) {
184
                case 'bearer':
185
                    $container->register($authServiceKey, Bearer::class)
186
                        ->addArgument($values['token']);
187
                    break;
188
                case 'basic':
189
                    $container->register($authServiceKey, BasicAuth::class)
190
                        ->addArgument($values['username'])
191
                        ->addArgument($values['password']);
192
                    break;
193
                case 'wsse':
194
                    $container->register($authServiceKey, Wsse::class)
195
                        ->addArgument($values['username'])
196
                        ->addArgument($values['password']);
197
                    break;
198
                case 'service':
199
                    $authServiceKey = $values['service'];
200
                    break;
201
                default:
202
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
203
            }
204
205
            $container->register('httplug.plugin.authentication.'.$name, AuthenticationPlugin::class)
206
                ->addArgument(new Reference($authServiceKey));
207
        }
208
    }
209
210
    /**
211
     * @param ContainerBuilder $container
212
     * @param string           $name
213
     * @param array            $arguments
214
     * @param bool             $profiling
215
     */
216
    private function configureClient(ContainerBuilder $container, $name, array $arguments, $profiling)
217
    {
218
        $serviceId = 'httplug.client.'.$name;
219
220
        $plugins = $arguments['plugins'];
221
        $pluginClientOptions = [];
222
223
        if ($profiling) {
224
            if (!in_array('httplug.plugin.stopwatch', $arguments['plugins'])) {
225
                // Add the stopwatch plugin
226
                array_unshift($arguments['plugins'], 'httplug.plugin.stopwatch');
227
            }
228
229
            // Tell the plugin journal what plugins we used
230
            $container
231
                ->getDefinition('httplug.collector.plugin_journal')
232
                ->addMethodCall('setPlugins', [$name, $arguments['plugins']])
233
            ;
234
235
            $debugPluginServiceId = $this->registerDebugPlugin($container, $serviceId);
236
237
            $pluginClientOptions['debug_plugins'] = [new Reference($debugPluginServiceId)];
238
        }
239
240
        if (array_key_exists('options', $arguments)) {
241
            foreach ($arguments['options'] as $option => $value) {
242
                switch ($option) {
243 View Code Duplication
                    case 'default_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...
244
                        $uriService = $serviceId.'.default_host_uri';
245
                        $addHostPlugin = $serviceId.'.default_host_plugin';
246
                        $this->createUri($container, $uriService, $value);
247
                        $container
248
                            ->register($addHostPlugin, AddHostPlugin::class)
249
                            ->setPublic(false)
250
                            ->addArgument(new Reference($uriService))
251
                        ;
252
                        // add to end of plugins list unless explicitly configured
253
                        if (!in_array($addHostPlugin, $plugins)) {
254
                            $plugins[] = $addHostPlugin;
255
                        }
256
                        break;
257 View Code Duplication
                    case 'force_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...
258
                        $uriService = $serviceId.'.force_host_uri';
259
                        $addHostPlugin = $serviceId.'.force_host_plugin';
260
                        $this->createUri($container, $uriService, $value);
261
                        $container
262
                            ->register($addHostPlugin, AddHostPlugin::class)
263
                            ->setPublic(false)
264
                            ->addArgument(new Reference($uriService))
265
                            ->addArgument(['replace' => true])
266
                        ;
267
                        // add to end of plugins list unless explicitly configured
268
                        if (!in_array($addHostPlugin, $plugins)) {
269
                            $plugins[] = $addHostPlugin;
270
                        }
271
                        break;
272
                }
273
            }
274
        }
275
276
        $container
277
            ->register($serviceId, DummyClient::class)
278
            ->setFactory([PluginClientFactory::class, 'createPluginClient'])
279
            ->addArgument(
280
                array_map(
281
                    function ($id) {
282
                        return new Reference($id);
283
                    },
284
                    $plugins
285
                )
286
            )
287
            ->addArgument(new Reference($arguments['factory']))
288
            ->addArgument($arguments['config'])
289
            ->addArgument($pluginClientOptions)
290
        ;
291
292
293
        /*
294
         * Decorate the client with clients from client-common
295
         */
296 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...
297
            $container
298
                ->register($serviceId.'.flexible', FlexibleHttpClient::class)
299
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
300
                ->setPublic(false)
301
                ->setDecoratedService($serviceId)
302
            ;
303
        }
304
305 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...
306
            $container
307
                ->register($serviceId.'.http_methods', HttpMethodsClient::class)
308
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
309
                ->setPublic(false)
310
                ->setDecoratedService($serviceId)
311
            ;
312
        }
313
314 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...
315
            $container
316
                ->register($serviceId.'.batch_client', BatchClient::class)
317
                ->setArguments([new Reference($serviceId.'.batch_client.inner')])
318
                ->setPublic(false)
319
                ->setDecoratedService($serviceId)
320
            ;
321
        }
322
    }
323
324
    /**
325
     * Create a URI object with the default URI factory.
326
     *
327
     * @param ContainerBuilder $container
328
     * @param string           $serviceId Name of the private service to create
329
     * @param string           $uri       String representation of the URI
330
     */
331
    private function createUri(ContainerBuilder $container, $serviceId, $uri)
332
    {
333
        $container
334
            ->register($serviceId, UriInterface::class)
335
            ->setPublic(false)
336
            ->setFactory([new Reference('httplug.uri_factory'), 'createUri'])
337
            ->addArgument($uri)
338
        ;
339
    }
340
341
    /**
342
     * Make the user can select what client is used for auto discovery. If none is provided, a service will be created
343
     * by finding a client using auto discovery.
344
     *
345
     * @param ContainerBuilder $container
346
     * @param array            $config
347
     */
348
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config)
349
    {
350
        $httpClient = $config['discovery']['client'];
351
352 View Code Duplication
        if (!empty($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...
353
            if ($httpClient === 'auto') {
354
                $httpClient = $this->registerAutoDiscoverableClient(
355
                    $container,
356
                    'auto_discovered_client',
357
                    [HttpClientDiscovery::class, 'find'],
358
                    $config['profiling']['enabled']
359
                );
360
            }
361
362
            $httpClient = new Reference($httpClient);
363
        }
364
365
        $asyncHttpClient = $config['discovery']['async_client'];
366
367 View Code Duplication
        if (!empty($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...
368
            if ($asyncHttpClient === 'auto') {
369
                $asyncHttpClient = $this->registerAutoDiscoverableClient(
370
                    $container,
371
                    'auto_discovered_async',
372
                    [HttpAsyncClientDiscovery::class, 'find'],
373
                    $config['profiling']['enabled']
374
                );
375
            }
376
377
            $asyncHttpClient = new Reference($asyncHttpClient);
378
        }
379
380
        $container
381
            ->getDefinition('httplug.strategy')
382
            ->addArgument($httpClient)
383
            ->addArgument($asyncHttpClient)
384
        ;
385
    }
386
387
    /**
388
     * Find a client with auto discovery and return a service Reference to it.
389
     *
390
     * @param ContainerBuilder $container
391
     * @param string           $name
392
     * @param callable         $factory
393
     * @param bool             $profiling
394
     *
395
     * @return string service id
396
     */
397
    private function registerAutoDiscoverableClient(ContainerBuilder $container, $name, $factory, $profiling)
398
    {
399
        $serviceId = 'httplug.auto_discovery.'.$name;
400
401
        $pluginClientOptions = [];
402
403
        if ($profiling) {
404
            // Tell the plugin journal what plugins we used
405
            $container
406
                ->getDefinition('httplug.collector.plugin_journal')
407
                ->addMethodCall('setPlugins', [$name, ['httplug.plugin.stopwatch']])
408
            ;
409
410
            $debugPluginServiceId = $this->registerDebugPlugin($container, $serviceId);
411
412
            $pluginClientOptions['debug_plugins'] = [new Reference($debugPluginServiceId)];
413
        }
414
415
        $container
416
            ->register($serviceId, DummyClient::class)
417
            ->setFactory([PluginClientFactory::class, 'createPluginClient'])
418
            ->setArguments([[new Reference('httplug.plugin.stopwatch')], $factory, [], $pluginClientOptions])
419
        ;
420
421
        return $serviceId;
422
    }
423
424
    /**
425
     * Create a new plugin service for this client.
426
     *
427
     * @param ContainerBuilder $container
428
     * @param string           $serviceId
429
     *
430
     * @return string
431
     */
432
    private function registerDebugPlugin(ContainerBuilder $container, $serviceId)
433
    {
434
        $serviceIdDebugPlugin = $serviceId.'.debug_plugin';
435
436
        $container
437
            ->register($serviceIdDebugPlugin, DebugPlugin::class)
438
            ->addArgument(new Reference('httplug.collector.debug_collector'))
439
            ->addArgument(substr($serviceId, strrpos($serviceId, '.') + 1))
440
            ->setPublic(false)
441
        ;
442
443
        return $serviceIdDebugPlugin;
444
    }
445
446
    /**
447
     * {@inheritdoc}
448
     */
449
    public function getConfiguration(array $config, ContainerBuilder $container)
450
    {
451
        return new Configuration($container->getParameter('kernel.debug'));
452
    }
453
}
454