Completed
Push — master ( 119f88...a5ef59 )
by Márk
06:54
created

HttplugExtension::configureClients()   B

Complexity

Conditions 5
Paths 9

Size

Total Lines 22
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

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