Completed
Pull Request — master (#97)
by Tobias
02:15
created

HttplugExtension::load()   C

Complexity

Conditions 7
Paths 48

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 7.0368

Importance

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