Completed
Pull Request — master (#96)
by Márk
05:25
created

HttplugExtension::load()   C

Complexity

Conditions 8
Paths 72

Size

Total Lines 40
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 9.4924

Importance

Changes 11
Bugs 3 Features 5
Metric Value
c 11
b 3
f 5
dl 0
loc 40
ccs 20
cts 28
cp 0.7143
rs 5.3846
cc 8
eloc 22
nc 72
nop 2
crap 9.4924
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\Client\Common\PluginClient;
9
use Http\Discovery\HttpAsyncClientDiscovery;
10
use Http\Discovery\HttpClientDiscovery;
11
use Http\HttplugBundle\ClientFactory\DummyClient;
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 3
    public function load(array $configs, ContainerBuilder $container)
33
    {
34 3
        $configuration = $this->getConfiguration($configs, $container);
35 3
        $config = $this->processConfiguration($configuration, $configs);
0 ignored issues
show
Documentation introduced by
$configuration is of type object|null, but the function expects a object<Symfony\Component...ConfigurationInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
36
37 3
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
38
39 3
        $loader->load('services.xml');
40 3
        $loader->load('plugins.xml');
41
42 3
        $toolbar = is_bool($config['toolbar']['enabled']) ? $config['toolbar']['enabled'] : $container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug');
43
44 3
        if ($toolbar) {
45
            $loader->load('data-collector.xml');
46
47
            if (!empty($config['toolbar']['formatter'])) {
48
                // Add custom formatter
49
                $container->getDefinition('httplug.collector.debug_collector')
50
                    ->replaceArgument(0, new Reference($config['toolbar']['formatter']));
51
            }
52
53
            $container->getDefinition('httplug.formatter.full_http_message')
54
                ->addArgument($config['toolbar']['captured_body_length']);
55
        }
56
57 3
        foreach ($config['classes'] as $service => $class) {
58 3
            if (!empty($class)) {
59 1
                $container->register(sprintf('httplug.%s.default', $service), $class);
60 1
            }
61 3
        }
62
63
        // Set main aliases
64 3
        foreach ($config['main_alias'] as $type => $id) {
65 3
            $container->setAlias(sprintf('httplug.%s', $type), $id);
66 3
        }
67
68 3
        $this->configurePlugins($container, $config['plugins']);
69 3
        $this->configureClients($container, $config, $toolbar);
70 3
        $this->configureAutoDiscoveryClients($container, $config, $toolbar);
71 3
    }
72
73
    /**
74
     * Configure client services.
75
     *
76
     * @param ContainerBuilder $container
77
     * @param array            $config
78
     * @param bool             $enableCollector
79
     */
80 3
    private function configureClients(ContainerBuilder $container, array $config, $enableCollector)
81
    {
82
        // If we have a client named 'default'
83 3
        $first = isset($config['clients']['default']) ? 'default' : null;
84
85 3
        foreach ($config['clients'] as $name => $arguments) {
86
            if ($first === null) {
87
                // Save the name of the first configurated client.
88
                $first = $name;
89
            }
90
91
            $this->configureClient($container, $name, $arguments, $enableCollector);
92 3
        }
93
94
        // If we have clients configured
95 3
        if ($first !== null) {
96
            if ($first !== 'default') {
97
                // Alias the first client to httplug.client.default
98
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
99
            }
100 3
        } elseif ($enableCollector) {
101
            $serviceIdDebugPlugin = $this->registerDebugPlugin($container, 'default');
102
            // No client was configured. Make sure to configure the auto discovery client with the PluginClient.
103
            $container->register('httplug.client', PluginClient::class)
104
                ->addArgument(new Reference('httplug.client.default'))
105
                ->addArgument([])
106
                ->addArgument(['debug_plugins' => [new Reference($serviceIdDebugPlugin)]]);
107
        }
108
    }
109
110
    /**
111
     * @param ContainerBuilder $container
112
     * @param array            $config
113
     */
114
    private function configurePlugins(ContainerBuilder $container, array $config)
115
    {
116
        if (!empty($config['authentication'])) {
117
            $this->configureAuthentication($container, $config['authentication']);
118
        }
119
        unset($config['authentication']);
120
121
        foreach ($config as $name => $pluginConfig) {
122
            $pluginId = 'httplug.plugin.'.$name;
123
124
            if ($pluginConfig['enabled']) {
125
                $def = $container->getDefinition($pluginId);
126
                $this->configurePluginByName($name, $def, $pluginConfig);
127
            } else {
128
                $container->removeDefinition($pluginId);
129
            }
130
        }
131
    }
132
133
    /**
134
     * @param string     $name
135
     * @param Definition $definition
136
     * @param array      $config
137
     */
138
    private function configurePluginByName($name, Definition $definition, array $config)
139
    {
140
        switch ($name) {
141
            case 'cache':
142
                $definition
143
                    ->replaceArgument(0, new Reference($config['cache_pool']))
144
                    ->replaceArgument(1, new Reference($config['stream_factory']))
145
                    ->replaceArgument(2, $config['config']);
146
                break;
147
            case 'cookie':
148
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
149
                break;
150
            case 'decoder':
151
                $definition->addArgument($config['use_content_encoding']);
152
                break;
153
            case 'history':
154
                $definition->replaceArgument(0, new Reference($config['journal']));
155
                break;
156
            case 'logger':
157
                $definition->replaceArgument(0, new Reference($config['logger']));
158
                if (!empty($config['formatter'])) {
159
                    $definition->replaceArgument(1, new Reference($config['formatter']));
160
                }
161
                break;
162
            case 'redirect':
163
                $definition
164
                    ->addArgument($config['preserve_header'])
165
                    ->addArgument($config['use_default_for_multiple']);
166
                break;
167
            case 'retry':
168
                $definition->addArgument($config['retry']);
169
                break;
170
            case 'stopwatch':
171
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
172
                break;
173
        }
174
    }
175
176
    /**
177
     * @param ContainerBuilder $container
178
     * @param array            $config
179
     */
180
    private function configureAuthentication(ContainerBuilder $container, array $config)
181
    {
182
        foreach ($config as $name => $values) {
183
            $authServiceKey = sprintf('httplug.plugin.authentication.%s.auth', $name);
184
            switch ($values['type']) {
185
                case 'bearer':
186
                    $container->register($authServiceKey, Bearer::class)
187
                        ->addArgument($values['token']);
188
                    break;
189
                case 'basic':
190
                    $container->register($authServiceKey, BasicAuth::class)
191
                        ->addArgument($values['username'])
192
                        ->addArgument($values['password']);
193
                    break;
194
                case 'wsse':
195
                    $container->register($authServiceKey, Wsse::class)
196
                        ->addArgument($values['username'])
197
                        ->addArgument($values['password']);
198
                    break;
199
                case 'service':
200
                    $authServiceKey = $values['service'];
201
                    break;
202
                default:
203
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
204
            }
205
206
            $container->register('httplug.plugin.authentication.'.$name, AuthenticationPlugin::class)
207
                ->addArgument(new Reference($authServiceKey));
208
        }
209
    }
210
211
    /**
212
     * @param ContainerBuilder $container
213
     * @param string           $name
214
     * @param array            $arguments
215
     * @param bool             $enableCollector
216
     */
217
    private function configureClient(ContainerBuilder $container, $name, array $arguments, $enableCollector)
218
    {
219
        $serviceId = 'httplug.client.'.$name;
220
        $def = $container->register($serviceId, DummyClient::class);
221
222
        // If there are no plugins nor should we use the data collector
223
        if (empty($arguments['plugins']) && !$enableCollector) {
224
            $def->setFactory([new Reference($arguments['factory']), 'createClient'])
225
                ->addArgument($arguments['config']);
226
        } else {
227
            $def
228
                ->setFactory('Http\HttplugBundle\ClientFactory\PluginClientFactory::createPluginClient')
229
                ->addArgument(
230
                    array_map(
231
                        function ($id) {
232
                            return new Reference($id);
233
                        },
234
                        $arguments['plugins']
235
                    )
236
                )
237
                ->addArgument(new Reference($arguments['factory']))
238
                ->addArgument($arguments['config'])
239
            ;
240
241
            if ($enableCollector) {
242
                $serviceIdDebugPlugin = $this->registerDebugPlugin($container, $name);
243
                $def->addArgument(['debug_plugins' => [new Reference($serviceIdDebugPlugin)]]);
244
245
                // tell the plugin journal what plugins we used
246
                $container->getDefinition('httplug.collector.plugin_journal')
247
                    ->addMethodCall('setPlugins', [$name, $arguments['plugins']]);
248
            }
249
        }
250
251
        /*
252
         * Decorate the client with clients from client-common
253
         */
254
255 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...
256
            $container->register($serviceId.'.flexible', FlexibleHttpClient::class)
257
                ->addArgument(new Reference($serviceId.'.flexible.inner'))
258
                ->setPublic(false)
259
                ->setDecoratedService($serviceId);
260
        }
261
262 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...
263
            $container->register($serviceId.'.http_methods', HttpMethodsClient::class)
264
                ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')])
265
                ->setPublic(false)
266
                ->setDecoratedService($serviceId);
267
        }
268
    }
269
270
    /**
271
     * Create a new plugin service for this client.
272
     *
273
     * @param ContainerBuilder $container
274
     * @param string           $name
275
     *
276
     * @return string
277
     */
278
    private function registerDebugPlugin(ContainerBuilder $container, $name)
279
    {
280
        $serviceIdDebugPlugin = 'httplug.client.'.$name.'.debug_plugin';
281
        $container->register($serviceIdDebugPlugin, DebugPlugin::class)
282
            ->addArgument(new Reference('httplug.collector.debug_collector'))
283
            ->addArgument($name)
284
            ->setPublic(false);
285
286
        return $serviceIdDebugPlugin;
287
    }
288
289
    /**
290
     * Make sure we inject the debug plugin for clients found by auto discovery.
291
     *
292
     * @param ContainerBuilder $container
293
     * @param array            $config
294
     * @param bool             $enableCollector
295
     */
296
    private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config, $enableCollector)
297
    {
298
        $httpClient = $config['discovery']['client'];
299 View Code Duplication
        if ($httpClient === 'auto') {
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...
300
            $httpClient = $this->registerAutoDiscoverableClientWithDebugPlugin(
301
                $container,
302
                'client',
303
                [HttpClientDiscovery::class, 'find'],
304
                $enableCollector
305
            );
306
        } elseif ($httpClient) {
307
            $httpClient = new Reference($httpClient);
308
        }
309
310
        $asyncHttpClient = $config['discovery']['async_client'];
311 View Code Duplication
        if ($asyncHttpClient === 'auto') {
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...
312
            $asyncHttpClient = $this->registerAutoDiscoverableClientWithDebugPlugin(
313
                $container,
314
                'async_client',
315
                [HttpAsyncClientDiscovery::class, 'find'],
316
                $enableCollector
317
            );
318
        } elseif ($asyncHttpClient) {
319
            $asyncHttpClient = new Reference($httpClient);
320
        }
321
322
        $container->getDefinition('httplug.strategy')
323
            ->addArgument($httpClient)
324
            ->addArgument($asyncHttpClient);
325
    }
326
327
    /**
328
     * @param ContainerBuilder $container
329
     * @param string           $name
330
     * @param callable         $factory
331
     * @param bool             $enableCollector
332
     *
333
     * @return Reference
334
     */
335
    private function registerAutoDiscoverableClientWithDebugPlugin(ContainerBuilder $container, $name, $factory, $enableCollector)
336
    {
337
        $definition = $container->register('httplug.auto_discovery_'.$name.'.pure', DummyClient::class);
338
        $definition->setPublic(false);
339
        $definition->setFactory($factory);
0 ignored issues
show
Documentation introduced by
$factory is of type callable, but the function expects a string|array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
340
341
        $pluginDefinition = $container
342
            ->register('httplug.auto_discovery_'.$name.'.plugin', PluginClient::class)
343
            ->setPublic(false)
344
            ->addArgument(new Reference('httplug.auto_discovery_'.$name.'.pure'))
345
            ->addArgument([])
346
        ;
347
348
        if ($enableCollector) {
349
            $serviceIdDebugPlugin = $this->registerDebugPlugin($container, 'auto_discovery_'.$name);
350
            $pluginDefinition->addArgument(['debug_plugins' => [new Reference($serviceIdDebugPlugin)]]);
351
        }
352
353
        return new Reference('httplug.auto_discovery_'.$name.'.plugin');
354
    }
355
}
356