Completed
Push — master ( 4c4e25...f034b1 )
by Fabien
13:54
created

HttplugExtension::configureAuthentication()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 41
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 10.5

Importance

Changes 0
Metric Value
dl 0
loc 41
ccs 14
cts 28
cp 0.5
rs 8.439
c 0
b 0
f 0
cc 6
eloc 29
nc 6
nop 3
crap 10.5
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\Client\Common\PluginClient;
10
use Http\Client\Common\PluginClientFactory;
11
use Http\Client\HttpClient;
12
use Http\Message\Authentication\BasicAuth;
13
use Http\Message\Authentication\Bearer;
14
use Http\Message\Authentication\Wsse;
15
use Psr\Http\Message\UriInterface;
16
use Symfony\Component\Config\FileLocator;
17
use Symfony\Component\DependencyInjection\ChildDefinition;
18
use Symfony\Component\DependencyInjection\ContainerBuilder;
19
use Symfony\Component\DependencyInjection\Definition;
20
use Symfony\Component\DependencyInjection\DefinitionDecorator;
21
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
22
use Symfony\Component\DependencyInjection\Reference;
23
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
24
25
/**
26
 * @author David Buchmann <[email protected]>
27
 * @author Tobias Nyholm <[email protected]>
28
 */
29
class HttplugExtension extends Extension
30
{
31
    /**
32
     * {@inheritdoc}
33
     */
34 13
    public function load(array $configs, ContainerBuilder $container)
35
    {
36 13
        $configuration = $this->getConfiguration($configs, $container);
37 13
        $config = $this->processConfiguration($configuration, $configs);
38
39 13
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
40
41 13
        $loader->load('services.xml');
42 13
        $loader->load('plugins.xml');
43
44
        // Register default services
45 13
        foreach ($config['classes'] as $service => $class) {
46 13
            if (!empty($class)) {
47 13
                $container->register(sprintf('httplug.%s.default', $service), $class);
48
            }
49
        }
50
51
        // Set main aliases
52 13
        foreach ($config['main_alias'] as $type => $id) {
53 13
            $container->setAlias(sprintf('httplug.%s', $type), $id);
54
        }
55
56
        // Configure toolbar
57 13
        $profilingEnabled = $this->isConfigEnabled($container, $config['profiling']);
58 13
        if ($profilingEnabled) {
59 12
            $loader->load('data-collector.xml');
60
61 12
            if (!empty($config['profiling']['formatter'])) {
62
                // Add custom formatter
63
                $container
64 1
                    ->getDefinition('httplug.collector.formatter')
65 1
                    ->replaceArgument(0, new Reference($config['profiling']['formatter']))
66
                ;
67
            }
68
69
            $container
70 12
                ->getDefinition('httplug.formatter.full_http_message')
71 12
                ->addArgument($config['profiling']['captured_body_length'])
72
            ;
73
        }
74
75 13
        $this->configureClients($container, $config);
76 13
        $this->configurePlugins($container, $config['plugins']); // must be after clients, as clients.X.plugins might use plugins as templates that will be removed
77 13
        $this->configureAutoDiscoveryClients($container, $config);
78 13
    }
79
80
    /**
81
     * Configure client services.
82
     *
83
     * @param ContainerBuilder $container
84
     * @param array            $config
85
     */
86 13
    private function configureClients(ContainerBuilder $container, array $config)
87
    {
88 13
        $first = null;
89 13
        $clients = [];
90
91 13
        foreach ($config['clients'] as $name => $arguments) {
92 6
            if (null === $first) {
93
                // Save the name of the first configured client.
94 6
                $first = $name;
95
            }
96
97 6
            $this->configureClient($container, $name, $arguments);
98 6
            $clients[] = $name;
99
        }
100
101
        // If we have clients configured
102 13
        if (null !== $first) {
103
            // If we do not have a client named 'default'
104 6
            if (!isset($config['clients']['default'])) {
105
                // Alias the first client to httplug.client.default
106 6
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
107
            }
108
        }
109 13
    }
110
111
    /**
112
     * Configure all Httplug plugins or remove their service definition.
113
     *
114
     * @param ContainerBuilder $container
115
     * @param array            $config
116
     */
117 13
    private function configurePlugins(ContainerBuilder $container, array $config)
118
    {
119 13
        if (!empty($config['authentication'])) {
120
            $this->configureAuthentication($container, $config['authentication']);
121
        }
122 13
        unset($config['authentication']);
123
124 13
        foreach ($config as $name => $pluginConfig) {
125 13
            $pluginId = 'httplug.plugin.'.$name;
126
127 13
            if ($this->isConfigEnabled($container, $pluginConfig)) {
128 13
                $def = $container->getDefinition($pluginId);
129 13
                $this->configurePluginByName($name, $def, $pluginConfig, $container, $pluginId);
130
            }
131
        }
132 13
    }
133
134
    /**
135
     * @param string           $name
136
     * @param Definition       $definition
137
     * @param array            $config
138
     * @param ContainerBuilder $container  In case we need to add additional services for this plugin
139
     * @param string           $serviceId  Service id of the plugin, in case we need to add additional services for this plugin.
140
     */
141 13
    private function configurePluginByName($name, Definition $definition, array $config, ContainerBuilder $container, $serviceId)
142
    {
143 13
        switch ($name) {
144
            case 'cache':
145 2
                $options = $config['config'];
146 2
                if (!empty($options['cache_key_generator'])) {
147 1
                    $options['cache_key_generator'] = new Reference($options['cache_key_generator']);
148
                }
149
150
                $definition
151 2
                    ->replaceArgument(0, new Reference($config['cache_pool']))
152 2
                    ->replaceArgument(1, new Reference($config['stream_factory']))
153 2
                    ->replaceArgument(2, $options);
154
155 2
                break;
156
            case 'cookie':
157
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
158
159
                break;
160
            case 'decoder':
161 13
                $definition->addArgument([
162 13
                    'use_content_encoding' => $config['use_content_encoding'],
163
                ]);
164
165 13
                break;
166
            case 'history':
167
                $definition->replaceArgument(0, new Reference($config['journal']));
168
169
                break;
170
            case 'logger':
171 13
                $definition->replaceArgument(0, new Reference($config['logger']));
172 13
                if (!empty($config['formatter'])) {
173
                    $definition->replaceArgument(1, new Reference($config['formatter']));
174
                }
175
176 13
                break;
177
            case 'redirect':
178 13
                $definition->addArgument([
179 13
                    'preserve_header' => $config['preserve_header'],
180 13
                    'use_default_for_multiple' => $config['use_default_for_multiple'],
181
                ]);
182
183 13
                break;
184
            case 'retry':
185 13
                $definition->addArgument([
186 13
                    'retries' => $config['retry'],
187
                ]);
188
189 13
                break;
190
            case 'stopwatch':
191 13
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
192
193 13
                break;
194
195
            /* client specific plugins */
196
197
            case 'add_host':
198 5
                $uriService = $serviceId.'.host_uri';
199 5
                $this->createUri($container, $uriService, $config['host']);
200 5
                $definition->replaceArgument(0, new Reference($uriService));
201 5
                $definition->replaceArgument(1, [
202 5
                    'replace' => $config['replace'],
203
                ]);
204
205 5
                break;
206
            case 'header_append':
207
            case 'header_defaults':
208
            case 'header_set':
209
            case 'header_remove':
210 1
                $definition->replaceArgument(0, $config['headers']);
211
212 1
                break;
213
214
            default:
215
                throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name));
216
        }
217 13
    }
218
219
    /**
220
     * @param ContainerBuilder $container
221
     * @param array            $config
222
     *
223
     * @return array List of service ids for the authentication plugins.
224
     */
225 5
    private function configureAuthentication(ContainerBuilder $container, array $config, $servicePrefix = 'httplug.plugin.authentication')
226
    {
227 5
        $pluginServices = [];
228
229 5
        foreach ($config as $name => $values) {
230 5
            $authServiceKey = sprintf($servicePrefix.'.%s.auth', $name);
231 5
            switch ($values['type']) {
232
                case 'bearer':
233
                    $container->register($authServiceKey, Bearer::class)
234
                        ->addArgument($values['token']);
235
236
                    break;
237
                case 'basic':
238 5
                    $container->register($authServiceKey, BasicAuth::class)
239 5
                        ->addArgument($values['username'])
240 5
                        ->addArgument($values['password']);
241
242 5
                    break;
243
                case 'wsse':
244
                    $container->register($authServiceKey, Wsse::class)
245
                        ->addArgument($values['username'])
246
                        ->addArgument($values['password']);
247
248
                    break;
249
                case 'service':
250
                    $authServiceKey = $values['service'];
251
252
                    break;
253
                default:
254
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
255
            }
256
257 5
            $pluginServiceKey = $servicePrefix.'.'.$name;
258 5
            $container->register($pluginServiceKey, AuthenticationPlugin::class)
259 5
                ->addArgument(new Reference($authServiceKey))
260
            ;
261 5
            $pluginServices[] = $pluginServiceKey;
262
        }
263
264 5
        return $pluginServices;
265
    }
266
267
    /**
268
     * @param ContainerBuilder $container
269
     * @param string           $clientName
270
     * @param array            $arguments
271
     */
272 6
    private function configureClient(ContainerBuilder $container, $clientName, array $arguments)
273
    {
274 6
        $serviceId = 'httplug.client.'.$clientName;
275
276 6
        $plugins = [];
277 6
        foreach ($arguments['plugins'] as $plugin) {
278 6
            $pluginName = key($plugin);
279 6
            $pluginConfig = current($plugin);
280 6
            if ('reference' === $pluginName) {
281 6
                $plugins[] = $pluginConfig['id'];
282 5
            } elseif ('authentication' === $pluginName) {
283 5
                $plugins = array_merge($plugins, $this->configureAuthentication($container, $pluginConfig, $serviceId.'.authentication'));
284
            } else {
285 6
                $plugins[] = $this->configurePlugin($container, $serviceId, $pluginName, $pluginConfig);
286
            }
287
        }
288
289
        $container
290 6
            ->register($serviceId.'.client', HttpClient::class)
291 6
            ->setFactory([new Reference($arguments['factory']), 'createClient'])
292 6
            ->addArgument($arguments['config'])
293 6
            ->setPublic(false)
294
        ;
295
296
        $container
297 6
            ->register($serviceId, PluginClient::class)
298 6
            ->setFactory([new Reference(PluginClientFactory::class), 'createClient'])
299 6
            ->addArgument(new Reference($serviceId.'.client'))
300 6
            ->addArgument(
301 6
                array_map(
302 6
                    function ($id) {
303 6
                        return new Reference($id);
304 6
                    },
305 6
                    $plugins
306
                )
307
            )
308 6
            ->addArgument([
309 6
                'client_name' => $clientName,
310
            ])
311
        ;
312
313
        /*
314
         * Decorate the client with clients from client-common
315
         */
316 6 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...
317
            $container
318
                ->register($serviceId.'.flexible', FlexibleHttpClient::class)
319
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
320
                ->setPublic(false)
321
                ->setDecoratedService($serviceId)
322
            ;
323
        }
324
325 6 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...
326
            $container
327
                ->register($serviceId.'.http_methods', HttpMethodsClient::class)
328
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
329
                ->setPublic(false)
330
                ->setDecoratedService($serviceId)
331
            ;
332
        }
333
334 6 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...
335
            $container
336
                ->register($serviceId.'.batch_client', BatchClient::class)
337
                ->setArguments([new Reference($serviceId.'.batch_client.inner')])
338
                ->setPublic(false)
339
                ->setDecoratedService($serviceId)
340
            ;
341
        }
342 6
    }
343
344
    /**
345
     * Create a URI object with the default URI factory.
346
     *
347
     * @param ContainerBuilder $container
348
     * @param string           $serviceId Name of the private service to create
349
     * @param string           $uri       String representation of the URI
350
     */
351 5
    private function createUri(ContainerBuilder $container, $serviceId, $uri)
352
    {
353
        $container
354 5
            ->register($serviceId, UriInterface::class)
355 5
            ->setPublic(false)
356 5
            ->setFactory([new Reference('httplug.uri_factory'), 'createUri'])
357 5
            ->addArgument($uri)
358
        ;
359 5
    }
360
361
    /**
362
     * Make the user can select what client is used for auto discovery. If none is provided, a service will be created
363
     * by finding a client using auto discovery.
364
     *
365
     * @param ContainerBuilder $container
366
     * @param array            $config
367
     */
368 13
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config)
369
    {
370 13
        $httpClient = $config['discovery']['client'];
371 13 View Code Duplication
        if ('auto' !== $httpClient) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
372 2
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_client');
373 2
            $container->removeDefinition('httplug.collector.auto_discovered_client');
374
375 2
            if (!empty($httpClient)) {
376 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_client', $httpClient);
377 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_client')->setPublic(false);
378
            }
379
        }
380
381 13
        $asyncHttpClient = $config['discovery']['async_client'];
382 13 View Code Duplication
        if ('auto' !== $asyncHttpClient) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
383 11
            $container->removeDefinition('httplug.auto_discovery.auto_discovered_async');
384 11
            $container->removeDefinition('httplug.collector.auto_discovered_async');
385
386 11
            if (!empty($asyncHttpClient)) {
387 1
                $container->setAlias('httplug.auto_discovery.auto_discovered_async', $asyncHttpClient);
388 1
                $container->getAlias('httplug.auto_discovery.auto_discovered_async')->setPublic(false);
389
            }
390
        }
391
392 13
        if (null === $httpClient && null === $asyncHttpClient) {
393 1
            $container->removeDefinition('httplug.strategy');
394
395 1
            return;
396
        }
397 12
    }
398
399
    /**
400
     * {@inheritdoc}
401
     */
402 13
    public function getConfiguration(array $config, ContainerBuilder $container)
403
    {
404 13
        return new Configuration($container->getParameter('kernel.debug'));
405
    }
406
407
    /**
408
     * Configure a plugin using the parent definition from plugins.xml.
409
     *
410
     * @param ContainerBuilder $container
411
     * @param string           $serviceId
412
     * @param string           $pluginName
413
     * @param array            $pluginConfig
414
     *
415
     * @return string configured service id
416
     */
417 5
    private function configurePlugin(ContainerBuilder $container, $serviceId, $pluginName, array $pluginConfig)
418
    {
419 5
        $pluginServiceId = $serviceId.'.plugin.'.$pluginName;
420
421 5
        $definition = class_exists(ChildDefinition::class)
422 5
            ? new ChildDefinition('httplug.plugin.'.$pluginName)
423 5
            : new DefinitionDecorator('httplug.plugin.'.$pluginName);
424
425 5
        $this->configurePluginByName($pluginName, $definition, $pluginConfig, $container, $pluginServiceId);
426 5
        $container->setDefinition($pluginServiceId, $definition);
427
428 5
        return $pluginServiceId;
429
    }
430
}
431