Completed
Pull Request — master (#118)
by David
07:52
created

HttplugExtension::configurePluginByName()   D

Complexity

Conditions 10
Paths 10

Size

Total Lines 45
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 28
CRAP Score 13.1885

Importance

Changes 4
Bugs 1 Features 1
Metric Value
c 4
b 1
f 1
dl 0
loc 45
ccs 28
cts 41
cp 0.6828
rs 4.8196
cc 10
eloc 37
nc 10
nop 3
crap 13.1885

How to fix   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\Discovery\HttpAsyncClientDiscovery;
10
use Http\Discovery\HttpClientDiscovery;
11
use Http\HttplugBundle\ClientFactory\DummyClient;
12
use Http\HttplugBundle\ClientFactory\PluginClientFactory;
13
use Http\HttplugBundle\Collector\DebugPlugin;
14
use Http\Message\Authentication\BasicAuth;
15
use Http\Message\Authentication\Bearer;
16
use Http\Message\Authentication\Wsse;
17
use Symfony\Component\Config\FileLocator;
18
use Symfony\Component\DependencyInjection\ContainerBuilder;
19
use Symfony\Component\DependencyInjection\Definition;
20
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
21
use Symfony\Component\DependencyInjection\Reference;
22
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
23
24
/**
25
 * @author David Buchmann <[email protected]>
26
 * @author Tobias Nyholm <[email protected]>
27
 */
28
class HttplugExtension extends Extension
29
{
30
    /**
31
     * {@inheritdoc}
32
     */
33 8
    public function load(array $configs, ContainerBuilder $container)
34
    {
35 8
        $configuration = $this->getConfiguration($configs, $container);
36 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 35 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...
37
38 8
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
39
40 8
        $loader->load('services.xml');
41 8
        $loader->load('plugins.xml');
42
43
        // Register default services
44 8
        foreach ($config['classes'] as $service => $class) {
45 8
            if (!empty($class)) {
46 1
                $container->register(sprintf('httplug.%s.default', $service), $class);
47 1
            }
48 8
        }
49
50
        // Set main aliases
51 8
        foreach ($config['main_alias'] as $type => $id) {
52 8
            $container->setAlias(sprintf('httplug.%s', $type), $id);
53 8
        }
54
55
        // Configure toolbar
56 8
        if ($config['profiling']['enabled']) {
57 5
            $loader->load('data-collector.xml');
58
59 5
            if (!empty($config['profiling']['formatter'])) {
60
                // Add custom formatter
61
                $container
62
                    ->getDefinition('httplug.collector.debug_collector')
63
                    ->replaceArgument(0, new Reference($config['profiling']['formatter']))
64
                ;
65
            }
66
67
            $container
68 5
                ->getDefinition('httplug.formatter.full_http_message')
69 5
                ->addArgument($config['profiling']['captured_body_length'])
70
            ;
71 5
        }
72
73 8
        $this->configurePlugins($container, $config['plugins']);
74 8
        $this->configureClients($container, $config);
75 8
        $this->configureAutoDiscoveryClients($container, $config);
76 8
    }
77
78
    /**
79
     * Configure client services.
80
     *
81
     * @param ContainerBuilder $container
82
     * @param array            $config
83
     */
84 8
    private function configureClients(ContainerBuilder $container, array $config)
85
    {
86 8
        $first = null;
87
88 8
        foreach ($config['clients'] as $name => $arguments) {
89 5
            if ($first === null) {
90
                // Save the name of the first configurated client.
91 5
                $first = $name;
92 5
            }
93
94 5
            $this->configureClient($container, $name, $arguments, $config['profiling']['enabled']);
95 8
        }
96
97
        // If we have clients configured
98 8
        if ($first !== null) {
99
            // If we do not have a client named 'default'
100 5
            if (!isset($config['clients']['default'])) {
101
                // Alias the first client to httplug.client.default
102 5
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
103 5
            }
104 5
        }
105 8
    }
106
107
    /**
108
     * @param ContainerBuilder $container
109
     * @param array            $config
110
     */
111 8
    private function configurePlugins(ContainerBuilder $container, array $config)
112
    {
113 8
        if (!empty($config['authentication'])) {
114
            $this->configureAuthentication($container, $config['authentication']);
115
        }
116 8
        unset($config['authentication']);
117
118 8
        foreach ($config as $name => $pluginConfig) {
119 8
            $pluginId = 'httplug.plugin.'.$name;
120
121 8
            if ($pluginConfig['enabled']) {
122 8
                $def = $container->getDefinition($pluginId);
123 8
                $this->configurePluginByName($name, $def, $pluginConfig);
124 8
            } else {
125 8
                $container->removeDefinition($pluginId);
126
            }
127 8
        }
128 8
    }
129
130
    /**
131
     * @param string     $name
132
     * @param Definition $definition
133
     * @param array      $config
134
     */
135 8
    private function configurePluginByName($name, Definition $definition, array $config)
136
    {
137
        switch ($name) {
138 8
            case 'cache':
139
                $definition
140
                    ->replaceArgument(0, new Reference($config['cache_pool']))
141
                    ->replaceArgument(1, new Reference($config['stream_factory']))
142
                    ->replaceArgument(2, $config['config']);
143
                break;
144 8
            case 'cookie':
145
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
146
                break;
147 8
            case 'decoder':
148 8
                $definition->addArgument([
149 8
                    'use_content_encoding' => $config['use_content_encoding'],
150 8
                ]);
151 8
                break;
152 8
            case 'history':
153
                $definition->replaceArgument(0, new Reference($config['journal']));
154
                break;
155 8
            case 'logger':
156 8
                $definition->replaceArgument(0, new Reference($config['logger']));
157 8
                if (!empty($config['formatter'])) {
158
                    $definition->replaceArgument(1, new Reference($config['formatter']));
159
                }
160 8
                break;
161 8
            case 'redirect':
162 8
                $definition->addArgument([
163 8
                    'preserve_header' => $config['preserve_header'],
164 8
                    'use_default_for_multiple' => $config['use_default_for_multiple'],
165 8
                ]);
166 8
                break;
167 8
            case 'retry':
168 8
                $definition->addArgument([
169 8
                    'retries' => $config['retry'],
170 8
                ]);
171 8
                break;
172 8
            case 'stopwatch':
173 8
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
174 8
                break;
175
176
            default:
177
                throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name));
178
        }
179 8
    }
180
181
    /**
182
     * @param ContainerBuilder $container
183
     * @param array            $config
184
     */
185
    private function configureAuthentication(ContainerBuilder $container, array $config)
186
    {
187
        foreach ($config as $name => $values) {
188
            $authServiceKey = sprintf('httplug.plugin.authentication.%s.auth', $name);
189
            switch ($values['type']) {
190
                case 'bearer':
191
                    $container->register($authServiceKey, Bearer::class)
192
                        ->addArgument($values['token']);
193
                    break;
194
                case 'basic':
195
                    $container->register($authServiceKey, BasicAuth::class)
196
                        ->addArgument($values['username'])
197
                        ->addArgument($values['password']);
198
                    break;
199
                case 'wsse':
200
                    $container->register($authServiceKey, Wsse::class)
201
                        ->addArgument($values['username'])
202
                        ->addArgument($values['password']);
203
                    break;
204
                case 'service':
205
                    $authServiceKey = $values['service'];
206
                    break;
207
                default:
208
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
209
            }
210
211
            $container->register('httplug.plugin.authentication.'.$name, AuthenticationPlugin::class)
212
                ->addArgument(new Reference($authServiceKey));
213
        }
214
    }
215
216
    /**
217
     * @param ContainerBuilder $container
218
     * @param string           $name
219
     * @param array            $arguments
220
     * @param bool             $profiling
221
     */
222
    private function configureClient(ContainerBuilder $container, $name, array $arguments, $profiling)
223
    {
224
        $serviceId = 'httplug.client.'.$name;
225
226
        $pluginClientOptions = [];
227
228
        if ($profiling) {
229
            if (!in_array('httplug.plugin.stopwatch', $arguments['plugins'])) {
230
                // Add the stopwatch plugin
231
                array_unshift($arguments['plugins'], 'httplug.plugin.stopwatch');
232
            }
233
234
            // Tell the plugin journal what plugins we used
235
            $container
236
                ->getDefinition('httplug.collector.plugin_journal')
237
                ->addMethodCall('setPlugins', [$name, $arguments['plugins']])
238
            ;
239
240
            $debugPluginServiceId = $this->registerDebugPlugin($container, $serviceId);
241
242
            $pluginClientOptions['debug_plugins'] = [new Reference($debugPluginServiceId)];
243
        }
244
245
        $container
246
            ->register($serviceId, DummyClient::class)
247
            ->setFactory([PluginClientFactory::class, 'createPluginClient'])
248
            ->addArgument(
249
                array_map(
250
                    function ($id) {
251
                        return new Reference($id);
252
                    },
253
                    $arguments['plugins']
254
                )
255
            )
256
            ->addArgument(new Reference($arguments['factory']))
257
            ->addArgument($arguments['config'])
258
            ->addArgument($pluginClientOptions)
259
        ;
260
261
262
        /*
263
         * Decorate the client with clients from client-common
264
         */
265 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...
266
            $container
267
                ->register($serviceId.'.flexible', FlexibleHttpClient::class)
268
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
269
                ->setPublic(false)
270
                ->setDecoratedService($serviceId)
271
            ;
272
        }
273
274 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...
275
            $container
276
                ->register($serviceId.'.http_methods', HttpMethodsClient::class)
277
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
278
                ->setPublic(false)
279
                ->setDecoratedService($serviceId)
280
            ;
281
        }
282
283 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...
284
            $container
285
                ->register($serviceId.'.batch_client', BatchClient::class)
286
                ->setArguments([new Reference($serviceId.'.batch_client.inner')])
287
                ->setPublic(false)
288
                ->setDecoratedService($serviceId)
289
            ;
290
        }
291
    }
292
293
    /**
294
     * Make the user can select what client is used for auto discovery. If none is provided, a service will be created
295
     * by finding a client using auto discovery.
296
     *
297
     * @param ContainerBuilder $container
298
     * @param array            $config
299
     */
300
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config)
301
    {
302
        $httpClient = $config['discovery']['client'];
303
304 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...
305
            if ($httpClient === 'auto') {
306
                $httpClient = $this->registerAutoDiscoverableClient(
307
                    $container,
308
                    'auto_discovered_client',
309
                    [HttpClientDiscovery::class, 'find'],
310
                    $config['profiling']['enabled']
311
                );
312
            }
313
314
            $httpClient = new Reference($httpClient);
315
        }
316
317
        $asyncHttpClient = $config['discovery']['async_client'];
318
319 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...
320
            if ($asyncHttpClient === 'auto') {
321
                $asyncHttpClient = $this->registerAutoDiscoverableClient(
322
                    $container,
323
                    'auto_discovered_async',
324
                    [HttpAsyncClientDiscovery::class, 'find'],
325
                    $config['profiling']['enabled']
326
                );
327
            }
328
329
            $asyncHttpClient = new Reference($asyncHttpClient);
330
        }
331
332
        $container
333
            ->getDefinition('httplug.strategy')
334
            ->addArgument($httpClient)
335
            ->addArgument($asyncHttpClient)
336
        ;
337
    }
338
339
    /**
340
     * Find a client with auto discovery and return a service Reference to it.
341
     *
342
     * @param ContainerBuilder $container
343
     * @param string           $name
344
     * @param callable         $factory
345
     * @param bool             $profiling
346
     *
347
     * @return string service id
348
     */
349
    private function registerAutoDiscoverableClient(ContainerBuilder $container, $name, $factory, $profiling)
350
    {
351
        $serviceId = 'httplug.auto_discovery.'.$name;
352
353
        $pluginClientOptions = [];
354
355
        if ($profiling) {
356
            // Tell the plugin journal what plugins we used
357
            $container
358
                ->getDefinition('httplug.collector.plugin_journal')
359
                ->addMethodCall('setPlugins', [$name, ['httplug.plugin.stopwatch']])
360
            ;
361
362
            $debugPluginServiceId = $this->registerDebugPlugin($container, $serviceId);
363
364
            $pluginClientOptions['debug_plugins'] = [new Reference($debugPluginServiceId)];
365
        }
366
367
        $container
368
            ->register($serviceId, DummyClient::class)
369
            ->setFactory([PluginClientFactory::class, 'createPluginClient'])
370
            ->setArguments([[new Reference('httplug.plugin.stopwatch')], $factory, [], $pluginClientOptions])
371
        ;
372
373
        return $serviceId;
374
    }
375
376
    /**
377
     * Create a new plugin service for this client.
378
     *
379
     * @param ContainerBuilder $container
380
     * @param string           $serviceId
381
     *
382
     * @return string
383
     */
384
    private function registerDebugPlugin(ContainerBuilder $container, $serviceId)
385
    {
386
        $serviceIdDebugPlugin = $serviceId.'.debug_plugin';
387
388
        $container
389
            ->register($serviceIdDebugPlugin, DebugPlugin::class)
390
            ->addArgument(new Reference('httplug.collector.debug_collector'))
391
            ->addArgument(substr($serviceId, strrpos($serviceId, '.') + 1))
392
            ->setPublic(false)
393
        ;
394
395
        return $serviceIdDebugPlugin;
396
    }
397
398
    /**
399
     * {@inheritdoc}
400
     */
401
    public function getConfiguration(array $config, ContainerBuilder $container)
402
    {
403
        return new Configuration($container->getParameter('kernel.debug'));
404
    }
405
}
406