Completed
Pull Request — master (#54)
by Tobias
08:14
created

HttplugExtension   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 219
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 56.25%

Importance

Changes 10
Bugs 1 Features 4
Metric Value
wmc 41
c 10
b 1
f 4
lcom 1
cbo 6
dl 0
loc 219
ccs 27
cts 48
cp 0.5625
rs 8.2769

6 Methods

Rating   Name   Duplication   Size   Complexity  
D load() 0 44 9
D configureClients() 0 40 9
A configurePlugins() 0 17 4
D configurePluginByName() 0 37 10
B configureAuthentication() 0 30 6
A verifyDiscoveryInstalled() 0 14 3

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\Plugin\AuthenticationPlugin;
6
use Http\Client\Plugin\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 3
        $loader->load('discovery.xml');
37
38 3
        $enabled = is_bool($config['toolbar']['enabled']) ? $config['toolbar']['enabled'] : $container->hasParameter('kernel.debug') && $container->getParameter('kernel.debug');
39 3
        if ($enabled) {
40
            $loader->load('data-collector.xml');
41
            $config['_inject_collector_plugin'] = true;
42
43
            if (!empty($config['toolbar']['formatter'])) {
44
                $container->getDefinition('httplug.collector.message_journal')
45
                    ->replaceArgument(0, new Reference($config['toolbar']['formatter']));
46
            }
47
        }
48
49 3
        $useDiscovery = false;
50 3
        foreach ($config['classes'] as $service => $class) {
51 1
            if (!empty($class)) {
52 1
                $container->removeDefinition(sprintf('httplug.%s.default', $service));
53 1
                $container->register(sprintf('httplug.%s.default', $service), $class);
54 3
            } else {
55
                // we have to use discovery to find this class
56 3
                $useDiscovery = true;
57 3
            }
58 3
        }
59 3
60 3
        if ($useDiscovery) {
61 3
            $this->verifyDiscoveryInstalled($container);
62
        }
63
64
65
        foreach ($config['main_alias'] as $type => $id) {
66
            $container->setAlias(sprintf('httplug.%s', $type), $id);
67
        }
68
        $this->configurePlugins($container, $config['plugins']);
69 3
        $this->configureClients($container, $config);
70
    }
71 3
72 3
    /**
73
     * Configure client services.
74
     *
75
     * @param ContainerBuilder $container
76
     * @param array            $config
77
     */
78
    private function configureClients(ContainerBuilder $container, array $config)
79
    {
80
        $first = isset($config['clients']['default']) ? 'default' : null;
81
        foreach ($config['clients'] as $name => $arguments) {
82
            if ($first === null) {
83
                $first = $name;
84
            }
85
86
            if (isset($config['_inject_collector_plugin'])) {
87
                array_unshift($arguments['plugins'], 'httplug.collector.history_plugin');
88
            }
89
90
            $def = $container->register('httplug.client.'.$name, DummyClient::class);
91
92
            if (empty($arguments['plugins'])) {
93
                $def->setFactory([new Reference($arguments['factory']), 'createClient'])
94 3
                    ->addArgument($arguments['config']);
95
            } else {
96
                $def->setFactory('Http\HttplugBundle\ClientFactory\PluginClientFactory::createPluginClient')
97 3
                    ->addArgument(array_map(function ($id) {
98
                        return new Reference($id);
99
                    }, $arguments['plugins']))
100
                    ->addArgument(new Reference($arguments['factory']))
101
                    ->addArgument($arguments['config']);
102 3
            }
103
        }
104
105
        // If we have clients configured
106
        if ($first !== null) {
107
            if ($first !== 'default') {
108
                // Alias the first client to httplug.client.default
109
                $container->setAlias('httplug.client.default', 'httplug.client.'.$first);
110
            }
111
        } elseif (isset($config['_inject_collector_plugin'])) {
112
            // No client was configured. Make sure to inject history plugin to the auto discovery client.
113
            $container->register('httplug.client', PluginClient::class)
114
                ->addArgument(new Reference('httplug.client.default'))
115
                ->addArgument([new Reference('httplug.collector.history_plugin')]);
116
        }
117
    }
118
119
    /**
120
     * @param ContainerBuilder $container
121
     * @param array            $config
122
     */
123
    private function configurePlugins(ContainerBuilder $container, array $config)
124
    {
125
        if (!empty($config['authentication'])) {
126
            $this->configureAuthentication($container, $config['authentication']);
127
        }
128
        unset($config['authentication']);
129
130
        foreach ($config as $name => $pluginConfig) {
131
            $pluginId = 'httplug.plugin.'.$name;
132
            if ($pluginConfig['enabled']) {
133
                $def = $container->getDefinition($pluginId);
134
                $this->configurePluginByName($name, $def, $pluginConfig);
135
            } else {
136
                $container->removeDefinition($pluginId);
137
            }
138
        }
139
    }
140
141
    /**
142
     * @param string     $name
143
     * @param Definition $definition
144
     * @param array      $config
145
     */
146
    private function configurePluginByName($name, Definition $definition, array $config)
147
    {
148
        switch ($name) {
149
            case 'cache':
150
                $definition
151
                    ->replaceArgument(0, new Reference($config['cache_pool']))
152
                    ->replaceArgument(1, new Reference($config['stream_factory']))
153
                    ->replaceArgument(2, $config['config']);
154
                break;
155
            case 'cookie':
156
                $definition->replaceArgument(0, new Reference($config['cookie_jar']));
157
                break;
158
            case 'decoder':
159
                $definition->addArgument($config['use_content_encoding']);
160
                break;
161
            case 'history':
162
                $definition->replaceArgument(0, new Reference($config['journal']));
163
                break;
164
            case 'logger':
165
                $definition->replaceArgument(0, new Reference($config['logger']));
166
                if (!empty($config['formatter'])) {
167
                    $definition->replaceArgument(1, new Reference($config['formatter']));
168
                }
169
                break;
170
            case 'redirect':
171
                $definition
172
                    ->addArgument($config['preserve_header'])
173
                    ->addArgument($config['use_default_for_multiple']);
174
                break;
175
            case 'retry':
176
                $definition->addArgument($config['retry']);
177
                break;
178
            case 'stopwatch':
179
                $definition->replaceArgument(0, new Reference($config['stopwatch']));
180
                break;
181
        }
182
    }
183
184
    /**
185
     * @param ContainerBuilder $container
186
     * @param Definition       $parent
0 ignored issues
show
Bug introduced by
There is no parameter named $parent. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
187
     * @param array            $config
188
     */
189
    private function configureAuthentication(ContainerBuilder $container, array $config)
190
    {
191
        foreach ($config as $name => $values) {
192
            $authServiceKey = sprintf('httplug.plugin.authentication.%s.auth', $name);
193
            switch ($values['type']) {
194
                case 'bearer':
195
                    $container->register($authServiceKey, Bearer::class)
196
                        ->addArgument($values['token']);
197
                    break;
198
                case 'basic':
199
                    $container->register($authServiceKey, BasicAuth::class)
200
                        ->addArgument($values['username'])
201
                        ->addArgument($values['password']);
202
                    break;
203
                case 'wsse':
204
                    $container->register($authServiceKey, Wsse::class)
205
                        ->addArgument($values['username'])
206
                        ->addArgument($values['password']);
207
                    break;
208
                case 'service':
209
                    $authServiceKey = $values['service'];
210
                    break;
211
                default:
212
                    throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
213
            }
214
215
            $container->register('httplug.plugin.authentication.'.$name, AuthenticationPlugin::class)
216
                ->addArgument(new Reference($authServiceKey));
217
        }
218
    }
219
220
    /**
221
     * Verify that Puli is installed
222
     *
223
     * @param ContainerBuilder $container
224
     * @throws \Exception
225
     */
226
    private function verifyDiscoveryInstalled(ContainerBuilder $container)
227
    {
228
        $enabledBundles = $container->getParameter('kernel.bundles');
229
        if (!isset($enabledBundles['PuliSymfonyBundle'])) {
230
            throw new \Exception(
231
                'You need to install puli/symfony-bundle or add configuration at httplug.classes in order to use this bundle. Refer to http://some.doc'
232
            );
233
        }
234
235
        // .... OR
236
        if (defined('PULI_FACTORY')) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
237
            // Throw exception
238
        }
239
    }
240
}
241