HttplugExtension::configurePluginByName()   F
last analyzed

Complexity

Conditions 22
Paths 26

Size

Total Lines 131

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 64
CRAP Score 25.8719

Importance

Changes 0
Metric Value
dl 0
loc 131
ccs 64
cts 80
cp 0.8
rs 3.3333
c 0
b 0
f 0
cc 22
nc 26
nop 5
crap 25.8719

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
declare(strict_types=1);
4
5
namespace Http\HttplugBundle\DependencyInjection;
6
7
use Http\Client\Common\BatchClient;
8
use Http\Client\Common\BatchClientInterface;
9
use Http\Client\Common\FlexibleHttpClient;
10
use Http\Client\Common\HttpMethodsClient;
11
use Http\Client\Common\HttpMethodsClientInterface;
12
use Http\Client\Common\Plugin\AuthenticationPlugin;
13
use Http\Client\Common\PluginClient;
14
use Http\Client\Common\PluginClientFactory;
15
use Http\Client\HttpAsyncClient;
16
use Http\Client\HttpClient;
17
use Http\Client\Plugin\Vcr\RecordPlugin;
18
use Http\Client\Plugin\Vcr\ReplayPlugin;
19
use Http\Message\Authentication\BasicAuth;
20
use Http\Message\Authentication\Bearer;
21
use Http\Message\Authentication\QueryParam;
22
use Http\Message\Authentication\Wsse;
23
use Http\Mock\Client as MockClient;
24
use Psr\Http\Message\UriInterface;
25
use Symfony\Component\Config\FileLocator;
26
use Symfony\Component\DependencyInjection\Alias;
27
use Symfony\Component\DependencyInjection\ChildDefinition;
28
use Symfony\Component\DependencyInjection\ContainerBuilder;
29
use Symfony\Component\DependencyInjection\Definition;
30
use Symfony\Component\DependencyInjection\DefinitionDecorator;
31
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
32
use Symfony\Component\DependencyInjection\Reference;
33
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
34
use Twig\Environment as TwigEnvironment;
35
36
/**
37
 * @author David Buchmann <[email protected]>
38
 * @author Tobias Nyholm <[email protected]>
39
 */
40
class HttplugExtension extends Extension
41
{
42
    public const HTTPLUG_CLIENT_TAG = 'httplug.client';
43
44
    /**
45
     * Used to check is the VCR plugin is installed.
46
     *
47
     * @var bool
48
     */
49
    private $useVcrPlugin = false;
50
51
    /**
52
     * {@inheritdoc}
53
     */
54 37
    public function load(array $configs, ContainerBuilder $container)
55
    {
56 37
        $configuration = $this->getConfiguration($configs, $container);
57 37
        $config = $this->processConfiguration($configuration, $configs);
58
59 37
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
60
61 37
        $loader->load('services.xml');
62 37
        $loader->load('plugins.xml');
63 37
        if (\class_exists(MockClient::class)) {
64 37
            $loader->load('mock-client.xml');
65
        }
66
67
        // Register default services
68 37
        foreach ($config['classes'] as $service => $class) {
69 37
            if (!empty($class)) {
70 1
                $container->register(sprintf('httplug.%s.default', $service), $class);
71
            }
72
        }
73
74
        // Set main aliases
75 37
        foreach ($config['main_alias'] as $type => $id) {
76 37
            $container->setAlias(sprintf('httplug.%s', $type), new Alias($id, true));
77
        }
78
79
        // Configure toolbar
80 37
        $profilingEnabled = $this->isConfigEnabled($container, $config['profiling']);
81 37
        if ($profilingEnabled) {
82 34
            $loader->load('data-collector.xml');
83
84 34
            if (!empty($config['profiling']['formatter'])) {
85
                // Add custom formatter
86
                $container
87 1
                    ->getDefinition('httplug.collector.formatter')
88 1
                    ->replaceArgument(0, new Reference($config['profiling']['formatter']))
89
                ;
90
            }
91
92
            $container
93 34
                ->getDefinition('httplug.formatter.full_http_message')
94 34
                ->addArgument($config['profiling']['captured_body_length'])
95
            ;
96
97 34
            if (!class_exists(TwigEnvironment::class) && !class_exists(\Twig_Environment::class)) {
98
                $container->removeDefinition('httplug.collector.twig.http_message');
99
            }
100
        }
101
102 37
        $this->configureClients($container, $config);
103 37
        $this->configurePlugins($container, $config['plugins']); // must be after clients, as clients.X.plugins might use plugins as templates that will be removed
104 37
        $this->configureAutoDiscoveryClients($container, $config);
105
106 37
        if (!$config['default_client_autowiring']) {
107 1
            $container->removeAlias(HttpAsyncClient::class);
108 1
            $container->removeAlias(HttpClient::class);
109
        }
110
111 37
        if ($this->useVcrPlugin) {
112 5
            if (!\class_exists(RecordPlugin::class)) {
113
                throw new \Exception('You need to require the VCR plugin to be able to use it: "composer require --dev php-http/vcr-plugin".');
114
            }
115
116 5
            $loader->load('vcr-plugin.xml');
117
        }
118 37
    }
119
120
    /**
121
     * Configure client services.
122
     */
123 37
    private function configureClients(ContainerBuilder $container, array $config)
124
    {
125 37
        $first = null;
126 37
        $clients = [];
127
128 37
        foreach ($config['clients'] as $name => $arguments) {
129 26
            if (null === $first) {
130
                // Save the name of the first configured client.
131 26
                $first = $name;
132
            }
133
134 26
            $this->configureClient($container, $name, $arguments);
135 26
            $clients[] = $name;
136
        }
137
138
        // If we have clients configured
139 37
        if (null !== $first) {
140
            // If we do not have a client named 'default'
141 26
            if (!array_key_exists('default', $config['clients'])) {
142 26
                $serviceId = 'httplug.client.'.$first;
143
                // Alias the first client to httplug.client.default
144 26
                $container->setAlias('httplug.client.default', $serviceId);
145 26
                $default = $first;
146
            } else {
147
                $default = 'default';
148
                $serviceId = 'httplug.client.'.$default;
149
            }
150
151
            // Autowiring alias for special clients, if they are enabled on the default client
152 26
            if ($config['clients'][$default]['flexible_client']) {
153 2
                $container->setAlias(FlexibleHttpClient::class, $serviceId.'.flexible');
154
            }
155 26
            if ($config['clients'][$default]['http_methods_client']) {
156 2
                if (\interface_exists(HttpMethodsClientInterface::class)) {
157
                    // support for client-common 1.9
158 2
                    $container->setAlias(HttpMethodsClientInterface::class, $serviceId.'.http_methods');
159
                }
160
            }
161 26
            if ($config['clients'][$default]['batch_client']) {
162 2
                if (\interface_exists(BatchClientInterface::class)) {
163
                    // support for client-common 1.9
164 2
                    $container->setAlias(BatchClientInterface::class, $serviceId.'.batch_client');
165
                }
166
            }
167
        }
168 37
    }
169
170
    /**
171
     * Configure all Httplug plugins or remove their service definition.
172
     */
173 37
    private function configurePlugins(ContainerBuilder $container, array $config)
174
    {
175 37
        if (!empty($config['authentication'])) {
176
            $this->configureAuthentication($container, $config['authentication']);
177
        }
178 37
        unset($config['authentication']);
179
180 37
        foreach ($config as $name => $pluginConfig) {
181 37
            $pluginId = 'httplug.plugin.'.$name;
182
183 37
            if ($this->isConfigEnabled($container, $pluginConfig)) {
184 37
                $def = $container->getDefinition($pluginId);
185 37
                $this->configurePluginByName($name, $def, $pluginConfig, $container, $pluginId);
186
            }
187
        }
188 37
    }
189
190
    /**
191
     * @param string           $name
192
     * @param ContainerBuilder $container In case we need to add additional services for this plugin
193
     * @param string           $serviceId service id of the plugin, in case we need to add additional services for this plugin
194
     */
195 37
    private function configurePluginByName($name, Definition $definition, array $config, ContainerBuilder $container, $serviceId)
196
    {
197 37
        switch ($name) {
198 37
            case 'cache':
199 3
                $options = $config['config'];
200 3
                if (!empty($options['cache_key_generator'])) {
201 1
                    $options['cache_key_generator'] = new Reference($options['cache_key_generator']);
202
                }
203
204 3
                if (empty($options['blacklisted_paths'])) {
205 3
                    unset($options['blacklisted_paths']);
206
                }
207
208
                $options['cache_listeners'] = array_map(function (string $serviceName): Reference {
209 1
                    return new Reference($serviceName);
210 3
                }, $options['cache_listeners']);
211
212 3
                if (0 === count($options['cache_listeners'])) {
213 2
                    unset($options['cache_listeners']);
214
                }
215
216
                $definition
217 3
                    ->replaceArgument(0, new Reference($config['cache_pool']))
218 3
                    ->replaceArgument(1, new Reference($config['stream_factory']))
219 3
                    ->replaceArgument(2, $options)
220 3
                    ->setAbstract(false)
221
                ;
222
223 3
                break;
224
225 37
            case 'cookie':
226
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
227
228
                break;
229
230 37
            case 'decoder':
231 37
                $definition->addArgument([
232 37
                    'use_content_encoding' => $config['use_content_encoding'],
233
                ]);
234
235 37
                break;
236
237 37
            case 'history':
238
                $definition->replaceArgument(0, new Reference($config['journal']));
239
240
                break;
241
242 37
            case 'logger':
243 37
                $definition->replaceArgument(0, new Reference($config['logger']));
244 37
                if (!empty($config['formatter'])) {
245
                    $definition->replaceArgument(1, new Reference($config['formatter']));
246
                }
247 37
                $definition->setAbstract(false);
248
249 37
                break;
250
251 37
            case 'redirect':
252 37
                $definition->addArgument([
253 37
                    'preserve_header' => $config['preserve_header'],
254 37
                    'use_default_for_multiple' => $config['use_default_for_multiple'],
255
                ]);
256
257 37
                break;
258
259 37
            case 'retry':
260 37
                $definition->addArgument([
261 37
                    'retries' => $config['retry'],
262
                ]);
263
264 37
                break;
265
266 37
            case 'stopwatch':
267
                $definition
268 37
                    ->replaceArgument(0, new Reference($config['stopwatch']))
269 37
                    ->setAbstract(false)
270
                ;
271
272 37
                break;
273
274
            /* client specific plugins */
275
276 7
            case 'add_host':
277 6
                $hostUriService = $serviceId.'.host_uri';
278 6
                $this->createUri($container, $hostUriService, $config['host']);
279 6
                $definition->replaceArgument(0, new Reference($hostUriService));
280 6
                $definition->replaceArgument(1, [
281 6
                    'replace' => $config['replace'],
282
                ]);
283
284 6
                break;
285
286 2
            case 'add_path':
287
                $pathUriService = $serviceId.'.path_uri';
288
                $this->createUri($container, $pathUriService, $config['path']);
289
                $definition->replaceArgument(0, new Reference($pathUriService));
290
291
                break;
292
293 2
            case 'base_uri':
294
                $baseUriService = $serviceId.'.base_uri';
295
                $this->createUri($container, $baseUriService, $config['uri']);
296
                $definition->replaceArgument(0, new Reference($baseUriService));
297
                $definition->replaceArgument(1, [
298
                    'replace' => $config['replace'],
299
                ]);
300
301
                break;
302
303 2
            case 'content_type':
304 2
                unset($config['enabled']);
305 2
                $definition->replaceArgument(0, $config);
306
307 2
                break;
308
309 1
            case 'header_append':
310 1
            case 'header_defaults':
311 1
            case 'header_set':
312 1
            case 'header_remove':
313 1
                $definition->replaceArgument(0, $config['headers']);
314
315 1
                break;
316
317 1
            case 'query_defaults':
318 1
                $definition->replaceArgument(0, $config['parameters']);
319
320 1
                break;
321
322
            default:
323
                throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name));
324
        }
325 37
    }
326
327
    /**
328
     * @return array list of service ids for the authentication plugins
329
     */
330 6
    private function configureAuthentication(ContainerBuilder $container, array $config, $servicePrefix = 'httplug.plugin.authentication')
331
    {
332 6
        $pluginServices = [];
333
334 6
        foreach ($config as $name => $values) {
335 6
            $authServiceKey = sprintf($servicePrefix.'.%s.auth', $name);
336 6
            switch ($values['type']) {
337 6
                case 'bearer':
338
                    $container->register($authServiceKey, Bearer::class)
339
                        ->addArgument($values['token']);
340
341
                    break;
342 6
                case 'basic':
343 6
                    $container->register($authServiceKey, BasicAuth::class)
344 6
                        ->addArgument($values['username'])
345 6
                        ->addArgument($values['password']);
346
347 6
                    break;
348
                case 'wsse':
349
                    $container->register($authServiceKey, Wsse::class)
350
                        ->addArgument($values['username'])
351
                        ->addArgument($values['password']);
352
353
                    break;
354
                case 'query_param':
355
                    $container->register($authServiceKey, QueryParam::class)
356
                        ->addArgument($values['params']);
357
358
                    break;
359
                case 'service':
360
                    $authServiceKey = $values['service'];
361
362
                    break;
363
                default:
364
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
365
            }
366
367 6
            $pluginServiceKey = $servicePrefix.'.'.$name;
368 6
            $container->register($pluginServiceKey, AuthenticationPlugin::class)
369 6
                ->addArgument(new Reference($authServiceKey))
370
            ;
371 6
            $pluginServices[] = $pluginServiceKey;
372
        }
373
374 6
        return $pluginServices;
375
    }
376
377
    /**
378
     * @param string $clientName
379
     */
380 26
    private function configureClient(ContainerBuilder $container, $clientName, array $arguments)
381
    {
382 26
        $serviceId = 'httplug.client.'.$clientName;
383
384 26
        $plugins = [];
385 26
        foreach ($arguments['plugins'] as $plugin) {
386 15
            $pluginName = key($plugin);
387 15
            $pluginConfig = current($plugin);
388
389 15
            switch ($pluginName) {
390 15
                case 'reference':
391 9
                    $plugins[] = $pluginConfig['id'];
392 9
                    break;
393 12
                case 'authentication':
394 6
                    $plugins = array_merge($plugins, $this->configureAuthentication($container, $pluginConfig, $serviceId.'.authentication'));
395 6
                    break;
396 12
                case 'vcr':
397 5
                    $this->useVcrPlugin = true;
398 5
                    $plugins = array_merge($plugins, $this->configureVcrPlugin($container, $pluginConfig, $serviceId.'.vcr'));
399 5
                    break;
400
                default:
401 7
                    $plugins[] = $this->configurePlugin($container, $serviceId, $pluginName, $pluginConfig);
402
            }
403
        }
404
405 26
        if (empty($arguments['service'])) {
406
            $container
407 25
                ->register($serviceId.'.client', HttpClient::class)
408 25
                ->setFactory([new Reference($arguments['factory']), 'createClient'])
409 25
                ->addArgument($arguments['config'])
410 25
                ->setPublic(false);
411
        } else {
412
            $container
413 2
                ->setAlias($serviceId.'.client', new Alias($arguments['service'], false));
414
        }
415
416
        $definition = $container
417 26
            ->register($serviceId, PluginClient::class)
418 26
            ->setFactory([new Reference(PluginClientFactory::class), 'createClient'])
419 26
            ->addArgument(new Reference($serviceId.'.client'))
420 26
            ->addArgument(
421 26
                array_map(
422
                    function ($id) {
423 15
                        return new Reference($id);
424 26
                    },
425
                    $plugins
426
                )
427
            )
428 26
            ->addArgument([
429 26
                'client_name' => $clientName,
430
            ])
431 26
            ->addTag(self::HTTPLUG_CLIENT_TAG)
432
        ;
433
434 26
        if (is_bool($arguments['public'])) {
435 5
            $definition->setPublic($arguments['public']);
436
        }
437
438
        /*
439
         * Decorate the client with clients from client-common
440
         */
441 26
        if ($arguments['flexible_client']) {
442
            $container
443 2
                ->register($serviceId.'.flexible', FlexibleHttpClient::class)
444 2
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
445 2
                ->setPublic($arguments['public'] ? true : false)
446 2
                ->setDecoratedService($serviceId)
447
            ;
448
        }
449
450 26
        if ($arguments['http_methods_client']) {
451
            $container
452 2
                ->register($serviceId.'.http_methods', HttpMethodsClient::class)
453 2
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
454 2
                ->setPublic($arguments['public'] ? true : false)
455 2
                ->setDecoratedService($serviceId)
456
            ;
457
        }
458
459 26
        if ($arguments['batch_client']) {
460
            $container
461 2
                ->register($serviceId.'.batch_client', BatchClient::class)
462 2
                ->setArguments([new Reference($serviceId.'.batch_client.inner')])
463 2
                ->setPublic($arguments['public'] ? true : false)
464 2
                ->setDecoratedService($serviceId)
465
            ;
466
        }
467 26
    }
468
469
    /**
470
     * Create a URI object with the default URI factory.
471
     *
472
     * @param string $serviceId Name of the private service to create
473
     * @param string $uri       String representation of the URI
474
     */
475 6
    private function createUri(ContainerBuilder $container, $serviceId, $uri)
476
    {
477
        $container
478 6
            ->register($serviceId, UriInterface::class)
479 6
            ->setPublic(false)
480 6
            ->setFactory([new Reference('httplug.uri_factory'), 'createUri'])
481 6
            ->addArgument($uri)
482
        ;
483 6
    }
484
485
    /**
486
     * Make the user can select what client is used for auto discovery. If none is provided, a service will be created
487
     * by finding a client using auto discovery.
488
     */
489 37
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config)
490
    {
491 37
        $httpClient = $config['discovery']['client'];
492 37
        if ('auto' !== $httpClient) {
493 2
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_client');
494 2
            $container->removeDefinition('httplug.collector.auto_discovered_client');
495
496 2
            if (!empty($httpClient)) {
497 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_client', $httpClient);
498 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_client')->setPublic(false);
499
            }
500
        }
501
502 37
        $asyncHttpClient = $config['discovery']['async_client'];
503 37
        if ('auto' !== $asyncHttpClient) {
504 34
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_async');
505 34
            $container->removeDefinition('httplug.collector.auto_discovered_async');
506
507 34
            if (!empty($asyncHttpClient)) {
508 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_async', $asyncHttpClient);
509 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_async')->setPublic(false);
510
            }
511
        }
512
513 37
        if (null === $httpClient && null === $asyncHttpClient) {
514 1
            $container->removeDefinition('httplug.strategy');
515
516 1
            return;
517
        }
518 36
    }
519
520
    /**
521
     * {@inheritdoc}
522
     */
523 37
    public function getConfiguration(array $config, ContainerBuilder $container)
524
    {
525 37
        return new Configuration($container->getParameter('kernel.debug'));
526
    }
527
528
    /**
529
     * Configure a plugin using the parent definition from plugins.xml.
530
     *
531
     * @param string $serviceId
532
     * @param string $pluginName
533
     *
534
     * @return string configured service id
535
     */
536 7
    private function configurePlugin(ContainerBuilder $container, $serviceId, $pluginName, array $pluginConfig)
537
    {
538 7
        $pluginServiceId = $serviceId.'.plugin.'.$pluginName;
539
540 7
        $definition = $this->createChildDefinition('httplug.plugin.'.$pluginName);
541
542 7
        $this->configurePluginByName($pluginName, $definition, $pluginConfig, $container, $pluginServiceId);
543 7
        $container->setDefinition($pluginServiceId, $definition);
544
545 7
        return $pluginServiceId;
546
    }
547
548 5
    private function configureVcrPlugin(ContainerBuilder $container, array $config, $prefix)
549
    {
550 5
        $recorder = $config['recorder'];
551 5
        $recorderId = in_array($recorder, ['filesystem', 'in_memory']) ? 'httplug.plugin.vcr.recorder.'.$recorder : $recorder;
552 5
        $namingStrategyId = $config['naming_strategy'];
553 5
        $replayId = $prefix.'.replay';
554 5
        $recordId = $prefix.'.record';
555
556 5
        if ('filesystem' === $recorder) {
557 2
            $recorderDefinition = $this->createChildDefinition('httplug.plugin.vcr.recorder.filesystem');
558 2
            $recorderDefinition->replaceArgument(0, $config['fixtures_directory']);
559 2
            $recorderId = $prefix.'.recorder';
560
561 2
            $container->setDefinition($recorderId, $recorderDefinition);
562
        }
563
564 5
        if ('default' === $config['naming_strategy']) {
565 2
            $namingStrategyId = $prefix.'.naming_strategy';
566 2
            $namingStrategy = $this->createChildDefinition('httplug.plugin.vcr.naming_strategy.path');
567
568 2
            if (!empty($config['naming_strategy_options'])) {
569 1
                $namingStrategy->setArguments([$config['naming_strategy_options']]);
570
            }
571
572 2
            $container->setDefinition($namingStrategyId, $namingStrategy);
573
        }
574
575
        $arguments = [
576 5
            new Reference($namingStrategyId),
577 5
            new Reference($recorderId),
578
        ];
579 5
        $record = new Definition(RecordPlugin::class, $arguments);
580 5
        $replay = new Definition(ReplayPlugin::class, $arguments);
581 5
        $plugins = [];
582
583 5
        switch ($config['mode']) {
584 5
            case 'replay':
585 1
                $container->setDefinition($replayId, $replay);
586 1
                $plugins[] = $replayId;
587 1
                break;
588 4
            case 'replay_or_record':
589 3
                $replay->setArgument(2, false);
590 3
                $container->setDefinition($replayId, $replay);
591 3
                $container->setDefinition($recordId, $record);
592 3
                $plugins[] = $replayId;
593 3
                $plugins[] = $recordId;
594 3
                break;
595 1
            case 'record':
596 1
                $container->setDefinition($recordId, $record);
597 1
                $plugins[] = $recordId;
598 1
                break;
599
        }
600
601 5
        return $plugins;
602
    }
603
604
    /**
605
     * BC for old Symfony versions. Remove this method and use new ChildDefinition directly when we drop support for Symfony 2.
606
     *
607
     * @param string $parent the parent service id
608
     *
609
     * @return ChildDefinition|DefinitionDecorator
610
     */
611 9
    private function createChildDefinition($parent)
612
    {
613 9
        $definitionClass = class_exists(ChildDefinition::class) ? ChildDefinition::class : DefinitionDecorator::class;
614
615 9
        return new $definitionClass($parent);
616
    }
617
}
618