Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
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 |
||
29 | class HttplugExtension extends Extension |
||
30 | { |
||
31 | /** |
||
32 | * {@inheritdoc} |
||
33 | */ |
||
34 | 13 | public function load(array $configs, ContainerBuilder $container) |
|
79 | |||
80 | /** |
||
81 | * Configure client services. |
||
82 | * |
||
83 | * @param ContainerBuilder $container |
||
84 | * @param array $config |
||
85 | */ |
||
86 | 13 | private function configureClients(ContainerBuilder $container, array $config) |
|
110 | |||
111 | /** |
||
112 | * Configure all Httplug plugins or remove their service definition. |
||
113 | * |
||
114 | * @param ContainerBuilder $container |
||
115 | * @param array $config |
||
116 | */ |
||
117 | 13 | private function configurePlugins(ContainerBuilder $container, array $config) |
|
135 | |||
136 | /** |
||
137 | * @param string $name |
||
138 | * @param Definition $definition |
||
139 | * @param array $config |
||
140 | * @param ContainerBuilder $container In case we need to add additional services for this plugin |
||
141 | * @param string $serviceId Service id of the plugin, in case we need to add additional services for this plugin. |
||
142 | */ |
||
143 | 13 | private function configurePluginByName($name, Definition $definition, array $config, ContainerBuilder $container, $serviceId) |
|
144 | { |
||
145 | 13 | switch ($name) { |
|
146 | case 'cache': |
||
147 | 1 | $options = $config['config']; |
|
148 | 1 | if (!empty($options['cache_key_generator'])) { |
|
149 | 1 | $options['cache_key_generator'] = new Reference($options['cache_key_generator']); |
|
150 | } |
||
151 | |||
152 | $definition |
||
153 | 1 | ->replaceArgument(0, new Reference($config['cache_pool'])) |
|
154 | 1 | ->replaceArgument(1, new Reference($config['stream_factory'])) |
|
155 | 1 | ->replaceArgument(2, $options); |
|
156 | |||
157 | 1 | break; |
|
158 | case 'cookie': |
||
159 | $definition->replaceArgument(0, new Reference($config['cookie_jar'])); |
||
160 | |||
161 | break; |
||
162 | case 'decoder': |
||
163 | 13 | $definition->addArgument([ |
|
164 | 13 | 'use_content_encoding' => $config['use_content_encoding'], |
|
165 | ]); |
||
166 | |||
167 | 13 | break; |
|
168 | case 'history': |
||
169 | $definition->replaceArgument(0, new Reference($config['journal'])); |
||
170 | |||
171 | break; |
||
172 | case 'logger': |
||
173 | 13 | $definition->replaceArgument(0, new Reference($config['logger'])); |
|
174 | 13 | if (!empty($config['formatter'])) { |
|
175 | $definition->replaceArgument(1, new Reference($config['formatter'])); |
||
176 | } |
||
177 | |||
178 | 13 | break; |
|
179 | case 'redirect': |
||
180 | 13 | $definition->addArgument([ |
|
181 | 13 | 'preserve_header' => $config['preserve_header'], |
|
182 | 13 | 'use_default_for_multiple' => $config['use_default_for_multiple'], |
|
183 | ]); |
||
184 | |||
185 | 13 | break; |
|
186 | case 'retry': |
||
187 | 13 | $definition->addArgument([ |
|
188 | 13 | 'retries' => $config['retry'], |
|
189 | ]); |
||
190 | |||
191 | 13 | break; |
|
192 | case 'stopwatch': |
||
193 | 13 | $definition->replaceArgument(0, new Reference($config['stopwatch'])); |
|
194 | |||
195 | 13 | break; |
|
196 | |||
197 | /* client specific plugins */ |
||
198 | |||
199 | case 'add_host': |
||
200 | 5 | $uriService = $serviceId.'.host_uri'; |
|
201 | 5 | $this->createUri($container, $uriService, $config['host']); |
|
202 | 5 | $definition->replaceArgument(0, new Reference($uriService)); |
|
203 | 5 | $definition->replaceArgument(1, [ |
|
204 | 5 | 'replace' => $config['replace'], |
|
205 | ]); |
||
206 | |||
207 | 5 | break; |
|
208 | case 'header_append': |
||
209 | case 'header_defaults': |
||
210 | case 'header_set': |
||
211 | case 'header_remove': |
||
212 | 1 | $definition->replaceArgument(0, $config['headers']); |
|
213 | |||
214 | 1 | break; |
|
215 | |||
216 | default: |
||
217 | throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name)); |
||
218 | } |
||
219 | 13 | } |
|
220 | |||
221 | /** |
||
222 | * @param ContainerBuilder $container |
||
223 | * @param array $config |
||
224 | * |
||
225 | * @return array List of service ids for the authentication plugins. |
||
226 | */ |
||
227 | 5 | private function configureAuthentication(ContainerBuilder $container, array $config, $servicePrefix = 'httplug.plugin.authentication') |
|
228 | { |
||
229 | 5 | $pluginServices = []; |
|
230 | |||
231 | 5 | foreach ($config as $name => $values) { |
|
232 | 5 | $authServiceKey = sprintf($servicePrefix.'.%s.auth', $name); |
|
233 | 5 | switch ($values['type']) { |
|
234 | case 'bearer': |
||
235 | $container->register($authServiceKey, Bearer::class) |
||
236 | ->addArgument($values['token']); |
||
237 | |||
238 | break; |
||
239 | case 'basic': |
||
240 | 5 | $container->register($authServiceKey, BasicAuth::class) |
|
241 | 5 | ->addArgument($values['username']) |
|
242 | 5 | ->addArgument($values['password']); |
|
243 | |||
244 | 5 | break; |
|
245 | case 'wsse': |
||
246 | $container->register($authServiceKey, Wsse::class) |
||
247 | ->addArgument($values['username']) |
||
248 | ->addArgument($values['password']); |
||
249 | |||
250 | break; |
||
251 | case 'service': |
||
252 | $authServiceKey = $values['service']; |
||
253 | |||
254 | break; |
||
255 | default: |
||
256 | throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type'])); |
||
257 | } |
||
258 | |||
259 | 5 | $pluginServiceKey = $servicePrefix.'.'.$name; |
|
260 | 5 | $container->register($pluginServiceKey, AuthenticationPlugin::class) |
|
261 | 5 | ->addArgument(new Reference($authServiceKey)) |
|
262 | ; |
||
263 | 5 | $pluginServices[] = $pluginServiceKey; |
|
264 | } |
||
265 | |||
266 | 5 | return $pluginServices; |
|
267 | } |
||
268 | |||
269 | /** |
||
270 | * @param ContainerBuilder $container |
||
271 | * @param string $clientName |
||
272 | * @param array $arguments |
||
273 | */ |
||
274 | 6 | private function configureClient(ContainerBuilder $container, $clientName, array $arguments) |
|
345 | |||
346 | /** |
||
347 | * Create a URI object with the default URI factory. |
||
348 | * |
||
349 | * @param ContainerBuilder $container |
||
350 | * @param string $serviceId Name of the private service to create |
||
351 | * @param string $uri String representation of the URI |
||
352 | */ |
||
353 | 5 | private function createUri(ContainerBuilder $container, $serviceId, $uri) |
|
362 | |||
363 | /** |
||
364 | * Make the user can select what client is used for auto discovery. If none is provided, a service will be created |
||
365 | * by finding a client using auto discovery. |
||
366 | * |
||
367 | * @param ContainerBuilder $container |
||
368 | * @param array $config |
||
369 | */ |
||
370 | 13 | private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config) |
|
400 | |||
401 | /** |
||
402 | * {@inheritdoc} |
||
403 | */ |
||
404 | 13 | public function getConfiguration(array $config, ContainerBuilder $container) |
|
408 | |||
409 | /** |
||
410 | * Configure a plugin using the parent definition from plugins.xml. |
||
411 | * |
||
412 | * @param ContainerBuilder $container |
||
413 | * @param string $serviceId |
||
414 | * @param string $pluginName |
||
415 | * @param array $pluginConfig |
||
416 | * |
||
417 | * @return string configured service id |
||
418 | */ |
||
419 | 5 | private function configurePlugin(ContainerBuilder $container, $serviceId, $pluginName, array $pluginConfig) |
|
432 | } |
||
433 |
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.