Completed
Push — master ( 78a0a4...eabb68 )
by David
02:07 queued 18s
created

HttplugExtension::configurePluginByName()   F

Complexity

Conditions 22
Paths 26

Size

Total Lines 125

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 61
CRAP Score 26.3429

Importance

Changes 0
Metric Value
dl 0
loc 125
ccs 61
cts 77
cp 0.7922
rs 3.3333
c 0
b 0
f 0
cc 22
nc 26
nop 5
crap 26.3429

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