Completed
Pull Request — master (#72)
by Márk
19:18 queued 11:20
created

HttplugExtension   B

Complexity

Total Complexity 40

Size/Duplication

Total Lines 213
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 54.35%

Importance

Changes 11
Bugs 2 Features 4
Metric Value
wmc 40
c 11
b 2
f 4
lcom 1
cbo 6
dl 0
loc 213
ccs 25
cts 46
cp 0.5435
rs 8.2608

6 Methods

Rating   Name   Duplication   Size   Complexity  
D configureClients() 0 40 9
C load() 0 34 8
B configurePlugins() 0 22 5
D configurePluginByName() 0 37 10
B configureAuthentication() 0 30 6
A resolvePluginStatus() 0 10 2

How to fix   Complexity   

Complex Class

Complex classes like HttplugExtension often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use HttplugExtension, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Http\HttplugBundle\DependencyInjection;
4
5
use Http\Client\Common\Plugin\AuthenticationPlugin;
6
use Http\Client\Common\PluginClient;
7
use Http\HttplugBundle\ClientFactory\DummyClient;
8
use Http\Message\Authentication\BasicAuth;
9
use Http\Message\Authentication\Bearer;
10
use Http\Message\Authentication\Wsse;
11
use Symfony\Component\Config\FileLocator;
12
use Symfony\Component\DependencyInjection\ContainerBuilder;
13
use Symfony\Component\DependencyInjection\Definition;
14
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
15
use Symfony\Component\DependencyInjection\Reference;
16
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
17
18
/**
19
 * @author David Buchmann <[email protected]>
20
 * @author Tobias Nyholm <[email protected]>
21
 */
22
class HttplugExtension extends Extension
23
{
24
    /**
25
     * {@inheritdoc}
26
     */
27 3
    public function load(array $configs, ContainerBuilder $container)
28
    {
29 3
        $configuration = $this->getConfiguration($configs, $container);
30 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...
31
32 3
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
33
34 3
        $loader->load('services.xml');
35 3
        $loader->load('plugins.xml');
36
37 3
        $enabled = is_bool($config['toolbar']['enabled']) ? $config['toolbar']['enabled'] : $container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug');
38 3
        if ($enabled) {
39
            $loader->load('data-collector.xml');
40
            $config['_inject_collector_plugin'] = true;
41
42
            if (!empty($config['toolbar']['formatter'])) {
43
                $container->getDefinition('httplug.collector.message_journal')
44
                    ->replaceArgument(0, new Reference($config['toolbar']['formatter']));
45
            }
46
        }
47
48 3
        foreach ($config['classes'] as $service => $class) {
49 3
            if (!empty($class)) {
50 1
                $container->register(sprintf('httplug.%s.default', $service), $class);
51 1
            }
52 3
        }
53
54 3
        foreach ($config['main_alias'] as $type => $id) {
55 3
            $container->setAlias(sprintf('httplug.%s', $type), $id);
56 3
        }
57
58 3
        $this->configurePlugins($container, $config['plugins']);
59 3
        $this->configureClients($container, $config);
60 3
    }
61
62
    /**
63
     * Configure client services.
64
     *
65
     * @param ContainerBuilder $container
66
     * @param array            $config
67
     */
68 3
    private function configureClients(ContainerBuilder $container, array $config)
69
    {
70 3
        $first = isset($config['clients']['default']) ? 'default' : null;
71 3
        foreach ($config['clients'] as $name => $arguments) {
72
            if ($first === null) {
73
                $first = $name;
74
            }
75
76
            if (isset($config['_inject_collector_plugin'])) {
77
                array_unshift($arguments['plugins'], 'httplug.collector.history_plugin');
78
            }
79
80
            $def = $container->register('httplug.client.'.$name, DummyClient::class);
81
82
            if (empty($arguments['plugins'])) {
83
                $def->setFactory([new Reference($arguments['factory']), 'createClient'])
84
                    ->addArgument($arguments['config']);
85
            } else {
86
                $def->setFactory('Http\HttplugBundle\ClientFactory\PluginClientFactory::createPluginClient')
87
                    ->addArgument(array_map(function ($id) {
88
                        return new Reference($id);
89
                    }, $arguments['plugins']))
90
                    ->addArgument(new Reference($arguments['factory']))
91
                    ->addArgument($arguments['config']);
92
            }
93 3
        }
94
95
        // If we have clients configured
96 3
        if ($first !== null) {
97
            if ($first !== 'default') {
98
                // Alias the first client to httplug.client.default
99
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
100
            }
101 3
        } elseif (isset($config['_inject_collector_plugin'])) {
102
            // No client was configured. Make sure to inject history plugin to the auto discovery client.
103
            $container->register('httplug.client', PluginClient::class)
104
                ->addArgument(new Reference('httplug.client.default'))
105
                ->addArgument([new Reference('httplug.collector.history_plugin')]);
106
        }
107
    }
108
109
    /**
110
     * @param ContainerBuilder $container
111
     * @param array            $config
112
     */
113
    private function configurePlugins(ContainerBuilder $container, array $config)
114
    {
115
        if (!empty($config['authentication'])) {
116
            $this->configureAuthentication($container, $config['authentication']);
117
        }
118
        unset($config['authentication']);
119
120
        foreach ($config as $name => $pluginConfig) {
121
            $pluginId = 'httplug.plugin.'.$name;
122
123
            if ('auto' === $pluginConfig['enabled']) {
124
                $pluginConfig['enabled'] = $this->resolvePluginStatus($name, $pluginConfig);
125
            }
126
127
            if ($pluginConfig['enabled']) {
128
                $def = $container->getDefinition($pluginId);
129
                $this->configurePluginByName($name, $def, $pluginConfig);
130
            } else {
131
                $container->removeDefinition($pluginId);
132
            }
133
        }
134
    }
135
136
    /**
137
     * @param string     $name
138
     * @param Definition $definition
139
     * @param array      $config
140
     */
141
    private function configurePluginByName($name, Definition $definition, array $config)
142
    {
143
        switch ($name) {
144
            case 'cache':
145
                $definition
146
                    ->replaceArgument(0, new Reference($config['cache_pool']))
147
                    ->replaceArgument(1, new Reference($config['stream_factory']))
148
                    ->replaceArgument(2, $config['config']);
149
                break;
150
            case 'cookie':
151
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
152
                break;
153
            case 'decoder':
154
                $definition->addArgument($config['use_content_encoding']);
155
                break;
156
            case 'history':
157
                $definition->replaceArgument(0, new Reference($config['journal']));
158
                break;
159
            case 'logger':
160
                $definition->replaceArgument(0, new Reference($config['logger']));
161
                if (!empty($config['formatter'])) {
162
                    $definition->replaceArgument(1, new Reference($config['formatter']));
163
                }
164
                break;
165
            case 'redirect':
166
                $definition
167
                    ->addArgument($config['preserve_header'])
168
                    ->addArgument($config['use_default_for_multiple']);
169
                break;
170
            case 'retry':
171
                $definition->addArgument($config['retry']);
172
                break;
173
            case 'stopwatch':
174
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
175
                break;
176
        }
177
    }
178
179
    /**
180
     * @param ContainerBuilder $container
181
     * @param array            $config
182
     */
183
    private function configureAuthentication(ContainerBuilder $container, array $config)
184
    {
185
        foreach ($config as $name => $values) {
186
            $authServiceKey = sprintf('httplug.plugin.authentication.%s.auth', $name);
187
            switch ($values['type']) {
188
                case 'bearer':
189
                    $container->register($authServiceKey, Bearer::class)
190
                        ->addArgument($values['token']);
191
                    break;
192
                case 'basic':
193
                    $container->register($authServiceKey, BasicAuth::class)
194
                        ->addArgument($values['username'])
195
                        ->addArgument($values['password']);
196
                    break;
197
                case 'wsse':
198
                    $container->register($authServiceKey, Wsse::class)
199
                        ->addArgument($values['username'])
200
                        ->addArgument($values['password']);
201
                    break;
202
                case 'service':
203
                    $authServiceKey = $values['service'];
204
                    break;
205
                default:
206
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
207
            }
208
209
            $container->register('httplug.plugin.authentication.'.$name, AuthenticationPlugin::class)
210
                ->addArgument(new Reference($authServiceKey));
211
        }
212
    }
213
214
    /**
215
     * Resolve the plugin enabled status if it is 'auto'.
216
     *
217
     * Returns false if plugin has no auto status allowed.
218
     *
219
     * @param string $name
220
     * @param array  $pluginConfig
221
     *
222
     * @return bool
223
     */
224
    private function resolvePluginStatus($name, array $pluginConfig)
0 ignored issues
show
Unused Code introduced by
The parameter $pluginConfig is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
225
    {
226
        switch ($name) {
227
            case 'logger':
228
                return class_exists('Http\Client\Common\Plugin\LoggerPlugin');
229
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
230
        }
231
232
        return false;
233
    }
234
}
235