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 |
||
40 | class HttplugExtension extends Extension |
||
41 | { |
||
42 | public const HTTPLUG_CLIENT_TAG = 'httplug.client'; |
||
43 | |||
44 | /** |
||
45 | * Used to check is the VCR plugin is installed. |
||
46 | * |
||
47 | * @var bool |
||
48 | */ |
||
49 | private $useVcrPlugin = false; |
||
50 | |||
51 | /** |
||
52 | * {@inheritdoc} |
||
53 | */ |
||
54 | 37 | public function load(array $configs, ContainerBuilder $container) |
|
119 | |||
120 | /** |
||
121 | * Configure client services. |
||
122 | */ |
||
123 | 37 | private function configureClients(ContainerBuilder $container, array $config) |
|
124 | { |
||
125 | 37 | $first = null; |
|
126 | 37 | $clients = []; |
|
127 | |||
128 | 37 | foreach ($config['clients'] as $name => $arguments) { |
|
129 | 26 | if (null === $first) { |
|
130 | // Save the name of the first configured client. |
||
131 | 26 | $first = $name; |
|
132 | } |
||
133 | |||
134 | 26 | $this->configureClient($container, $name, $arguments); |
|
135 | 26 | $clients[] = $name; |
|
136 | } |
||
137 | |||
138 | // If we have clients configured |
||
139 | 37 | if (null !== $first) { |
|
140 | // If we do not have a client named 'default' |
||
141 | 26 | if (!array_key_exists('default', $config['clients'])) { |
|
142 | 26 | $serviceId = 'httplug.client.'.$first; |
|
143 | // Alias the first client to httplug.client.default |
||
144 | 26 | $container->setAlias('httplug.client.default', $serviceId); |
|
145 | 26 | $default = $first; |
|
146 | } else { |
||
147 | $default = 'default'; |
||
148 | $serviceId = 'httplug.client.'.$default; |
||
149 | } |
||
150 | |||
151 | // Autowiring alias for special clients, if they are enabled on the default client |
||
152 | 26 | if ($config['clients'][$default]['flexible_client']) { |
|
153 | 2 | $container->setAlias(FlexibleHttpClient::class, $serviceId.'.flexible'); |
|
154 | } |
||
155 | 26 | if ($config['clients'][$default]['http_methods_client']) { |
|
156 | 2 | if (\interface_exists(HttpMethodsClientInterface::class)) { |
|
157 | // support for client-common 1.9 |
||
158 | 2 | $container->setAlias(HttpMethodsClientInterface::class, $serviceId.'.http_methods'); |
|
159 | } |
||
160 | } |
||
161 | 26 | if ($config['clients'][$default]['batch_client']) { |
|
162 | 2 | if (\interface_exists(BatchClientInterface::class)) { |
|
163 | // support for client-common 1.9 |
||
164 | 2 | $container->setAlias(BatchClientInterface::class, $serviceId.'.batch_client'); |
|
165 | } |
||
166 | } |
||
167 | } |
||
168 | 37 | } |
|
169 | |||
170 | /** |
||
171 | * Configure all Httplug plugins or remove their service definition. |
||
172 | */ |
||
173 | 37 | private function configurePlugins(ContainerBuilder $container, array $config) |
|
174 | { |
||
175 | 37 | if (!empty($config['authentication'])) { |
|
176 | $this->configureAuthentication($container, $config['authentication']); |
||
177 | } |
||
178 | 37 | unset($config['authentication']); |
|
179 | |||
180 | 37 | foreach ($config as $name => $pluginConfig) { |
|
181 | 37 | $pluginId = 'httplug.plugin.'.$name; |
|
182 | |||
183 | 37 | if ($this->isConfigEnabled($container, $pluginConfig)) { |
|
184 | 37 | $def = $container->getDefinition($pluginId); |
|
185 | 37 | $this->configurePluginByName($name, $def, $pluginConfig, $container, $pluginId); |
|
186 | } |
||
187 | } |
||
188 | 37 | } |
|
189 | |||
190 | /** |
||
191 | * @param string $name |
||
192 | * @param ContainerBuilder $container In case we need to add additional services for this plugin |
||
193 | * @param string $serviceId service id of the plugin, in case we need to add additional services for this plugin |
||
194 | */ |
||
195 | 37 | private function configurePluginByName($name, Definition $definition, array $config, ContainerBuilder $container, $serviceId) |
|
196 | { |
||
197 | 37 | switch ($name) { |
|
198 | 37 | case 'cache': |
|
199 | 3 | $options = $config['config']; |
|
200 | 3 | if (!empty($options['cache_key_generator'])) { |
|
201 | 1 | $options['cache_key_generator'] = new Reference($options['cache_key_generator']); |
|
202 | } |
||
203 | |||
204 | 3 | if (empty($options['blacklisted_paths'])) { |
|
205 | 3 | unset($options['blacklisted_paths']); |
|
206 | } |
||
207 | |||
208 | $options['cache_listeners'] = array_map(function (string $serviceName): Reference { |
||
209 | 1 | return new Reference($serviceName); |
|
210 | 3 | }, $options['cache_listeners']); |
|
211 | |||
212 | 3 | if (0 === count($options['cache_listeners'])) { |
|
213 | 2 | unset($options['cache_listeners']); |
|
214 | } |
||
215 | |||
216 | $definition |
||
217 | 3 | ->replaceArgument(0, new Reference($config['cache_pool'])) |
|
218 | 3 | ->replaceArgument(1, new Reference($config['stream_factory'])) |
|
219 | 3 | ->replaceArgument(2, $options) |
|
220 | 3 | ->setAbstract(false) |
|
221 | ; |
||
222 | |||
223 | 3 | break; |
|
224 | |||
225 | 37 | case 'cookie': |
|
226 | $definition->replaceArgument(0, new Reference($config['cookie_jar'])); |
||
227 | |||
228 | break; |
||
229 | |||
230 | 37 | case 'decoder': |
|
231 | 37 | $definition->addArgument([ |
|
232 | 37 | 'use_content_encoding' => $config['use_content_encoding'], |
|
233 | ]); |
||
234 | |||
235 | 37 | break; |
|
236 | |||
237 | 37 | case 'history': |
|
238 | $definition->replaceArgument(0, new Reference($config['journal'])); |
||
239 | |||
240 | break; |
||
241 | |||
242 | 37 | case 'logger': |
|
243 | 37 | $definition->replaceArgument(0, new Reference($config['logger'])); |
|
244 | 37 | if (!empty($config['formatter'])) { |
|
245 | $definition->replaceArgument(1, new Reference($config['formatter'])); |
||
246 | } |
||
247 | 37 | $definition->setAbstract(false); |
|
248 | |||
249 | 37 | break; |
|
250 | |||
251 | 37 | case 'redirect': |
|
252 | 37 | $definition->addArgument([ |
|
253 | 37 | 'preserve_header' => $config['preserve_header'], |
|
254 | 37 | 'use_default_for_multiple' => $config['use_default_for_multiple'], |
|
255 | ]); |
||
256 | |||
257 | 37 | break; |
|
258 | |||
259 | 37 | case 'retry': |
|
260 | 37 | $definition->addArgument([ |
|
261 | 37 | 'retries' => $config['retry'], |
|
262 | ]); |
||
263 | |||
264 | 37 | break; |
|
265 | |||
266 | 37 | case 'stopwatch': |
|
267 | $definition |
||
268 | 37 | ->replaceArgument(0, new Reference($config['stopwatch'])) |
|
269 | 37 | ->setAbstract(false) |
|
270 | ; |
||
271 | |||
272 | 37 | break; |
|
273 | |||
274 | /* client specific plugins */ |
||
275 | |||
276 | 7 | case 'add_host': |
|
277 | 6 | $hostUriService = $serviceId.'.host_uri'; |
|
278 | 6 | $this->createUri($container, $hostUriService, $config['host']); |
|
279 | 6 | $definition->replaceArgument(0, new Reference($hostUriService)); |
|
280 | 6 | $definition->replaceArgument(1, [ |
|
281 | 6 | 'replace' => $config['replace'], |
|
282 | ]); |
||
283 | |||
284 | 6 | break; |
|
285 | |||
286 | 2 | case 'add_path': |
|
287 | $pathUriService = $serviceId.'.path_uri'; |
||
288 | $this->createUri($container, $pathUriService, $config['path']); |
||
289 | $definition->replaceArgument(0, new Reference($pathUriService)); |
||
290 | |||
291 | break; |
||
292 | |||
293 | 2 | case 'base_uri': |
|
294 | $baseUriService = $serviceId.'.base_uri'; |
||
295 | $this->createUri($container, $baseUriService, $config['uri']); |
||
296 | $definition->replaceArgument(0, new Reference($baseUriService)); |
||
297 | $definition->replaceArgument(1, [ |
||
298 | 'replace' => $config['replace'], |
||
299 | ]); |
||
300 | |||
301 | break; |
||
302 | |||
303 | 2 | case 'content_type': |
|
304 | 2 | unset($config['enabled']); |
|
305 | 2 | $definition->replaceArgument(0, $config); |
|
306 | |||
307 | 2 | break; |
|
308 | |||
309 | 1 | case 'header_append': |
|
310 | 1 | case 'header_defaults': |
|
311 | 1 | case 'header_set': |
|
312 | 1 | case 'header_remove': |
|
313 | 1 | $definition->replaceArgument(0, $config['headers']); |
|
314 | |||
315 | 1 | break; |
|
316 | |||
317 | 1 | case 'query_defaults': |
|
318 | 1 | $definition->replaceArgument(0, $config['parameters']); |
|
319 | |||
320 | 1 | break; |
|
321 | |||
322 | default: |
||
323 | throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name)); |
||
324 | } |
||
325 | 37 | } |
|
326 | |||
327 | /** |
||
328 | * @return array list of service ids for the authentication plugins |
||
329 | */ |
||
330 | 6 | private function configureAuthentication(ContainerBuilder $container, array $config, $servicePrefix = 'httplug.plugin.authentication') |
|
331 | { |
||
332 | 6 | $pluginServices = []; |
|
333 | |||
334 | 6 | foreach ($config as $name => $values) { |
|
335 | 6 | $authServiceKey = sprintf($servicePrefix.'.%s.auth', $name); |
|
336 | 6 | switch ($values['type']) { |
|
337 | 6 | case 'bearer': |
|
338 | $container->register($authServiceKey, Bearer::class) |
||
339 | ->addArgument($values['token']); |
||
340 | |||
341 | break; |
||
342 | 6 | case 'basic': |
|
343 | 6 | $container->register($authServiceKey, BasicAuth::class) |
|
344 | 6 | ->addArgument($values['username']) |
|
345 | 6 | ->addArgument($values['password']); |
|
346 | |||
347 | 6 | break; |
|
348 | case 'wsse': |
||
349 | $container->register($authServiceKey, Wsse::class) |
||
350 | ->addArgument($values['username']) |
||
351 | ->addArgument($values['password']); |
||
352 | |||
353 | break; |
||
354 | case 'query_param': |
||
355 | $container->register($authServiceKey, QueryParam::class) |
||
356 | ->addArgument($values['params']); |
||
357 | |||
358 | break; |
||
359 | case 'service': |
||
360 | $authServiceKey = $values['service']; |
||
361 | |||
362 | break; |
||
363 | default: |
||
364 | throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type'])); |
||
365 | } |
||
366 | |||
367 | 6 | $pluginServiceKey = $servicePrefix.'.'.$name; |
|
368 | 6 | $container->register($pluginServiceKey, AuthenticationPlugin::class) |
|
369 | 6 | ->addArgument(new Reference($authServiceKey)) |
|
370 | ; |
||
371 | 6 | $pluginServices[] = $pluginServiceKey; |
|
372 | } |
||
373 | |||
374 | 6 | return $pluginServices; |
|
375 | } |
||
376 | |||
377 | /** |
||
378 | * @param string $clientName |
||
379 | */ |
||
380 | 26 | private function configureClient(ContainerBuilder $container, $clientName, array $arguments) |
|
381 | { |
||
382 | 26 | $serviceId = 'httplug.client.'.$clientName; |
|
383 | |||
384 | 26 | $plugins = []; |
|
385 | 26 | foreach ($arguments['plugins'] as $plugin) { |
|
386 | 15 | $pluginName = key($plugin); |
|
387 | 15 | $pluginConfig = current($plugin); |
|
388 | |||
389 | 15 | switch ($pluginName) { |
|
390 | 15 | case 'reference': |
|
391 | 9 | $plugins[] = $pluginConfig['id']; |
|
392 | 9 | break; |
|
393 | 12 | case 'authentication': |
|
394 | 6 | $plugins = array_merge($plugins, $this->configureAuthentication($container, $pluginConfig, $serviceId.'.authentication')); |
|
395 | 6 | break; |
|
396 | 12 | case 'vcr': |
|
397 | 5 | $this->useVcrPlugin = true; |
|
398 | 5 | $plugins = array_merge($plugins, $this->configureVcrPlugin($container, $pluginConfig, $serviceId.'.vcr')); |
|
399 | 5 | break; |
|
400 | default: |
||
401 | 7 | $plugins[] = $this->configurePlugin($container, $serviceId, $pluginName, $pluginConfig); |
|
402 | } |
||
403 | } |
||
404 | |||
405 | 26 | if (empty($arguments['service'])) { |
|
406 | $container |
||
407 | 25 | ->register($serviceId.'.client', HttpClient::class) |
|
408 | 25 | ->setFactory([new Reference($arguments['factory']), 'createClient']) |
|
409 | 25 | ->addArgument($arguments['config']) |
|
410 | 25 | ->setPublic(false); |
|
411 | } else { |
||
412 | $container |
||
413 | 2 | ->setAlias($serviceId.'.client', new Alias($arguments['service'], false)); |
|
414 | } |
||
415 | |||
416 | $definition = $container |
||
417 | 26 | ->register($serviceId, PluginClient::class) |
|
418 | 26 | ->setFactory([new Reference(PluginClientFactory::class), 'createClient']) |
|
419 | 26 | ->addArgument(new Reference($serviceId.'.client')) |
|
420 | 26 | ->addArgument( |
|
421 | 26 | array_map( |
|
422 | function ($id) { |
||
423 | 15 | return new Reference($id); |
|
424 | 26 | }, |
|
425 | $plugins |
||
426 | ) |
||
427 | ) |
||
428 | 26 | ->addArgument([ |
|
429 | 26 | 'client_name' => $clientName, |
|
430 | ]) |
||
431 | 26 | ->addTag(self::HTTPLUG_CLIENT_TAG) |
|
432 | ; |
||
433 | |||
434 | 26 | if (is_bool($arguments['public'])) { |
|
435 | 5 | $definition->setPublic($arguments['public']); |
|
436 | } |
||
437 | |||
438 | /* |
||
439 | * Decorate the client with clients from client-common |
||
440 | */ |
||
441 | 26 | if ($arguments['flexible_client']) { |
|
442 | $container |
||
443 | 2 | ->register($serviceId.'.flexible', FlexibleHttpClient::class) |
|
444 | 2 | ->addArgument(new Reference($serviceId.'.flexible.inner')) |
|
445 | 2 | ->setPublic($arguments['public'] ? true : false) |
|
446 | 2 | ->setDecoratedService($serviceId) |
|
447 | ; |
||
448 | } |
||
449 | |||
450 | 26 | if ($arguments['http_methods_client']) { |
|
451 | $container |
||
452 | 2 | ->register($serviceId.'.http_methods', HttpMethodsClient::class) |
|
453 | 2 | ->setArguments([new Reference($serviceId.'.http_methods.inner'), new Reference('httplug.message_factory')]) |
|
454 | 2 | ->setPublic($arguments['public'] ? true : false) |
|
455 | 2 | ->setDecoratedService($serviceId) |
|
456 | ; |
||
457 | } |
||
458 | |||
459 | 26 | if ($arguments['batch_client']) { |
|
460 | $container |
||
461 | 2 | ->register($serviceId.'.batch_client', BatchClient::class) |
|
462 | 2 | ->setArguments([new Reference($serviceId.'.batch_client.inner')]) |
|
463 | 2 | ->setPublic($arguments['public'] ? true : false) |
|
464 | 2 | ->setDecoratedService($serviceId) |
|
465 | ; |
||
466 | } |
||
467 | 26 | } |
|
468 | |||
469 | /** |
||
470 | * Create a URI object with the default URI factory. |
||
471 | * |
||
472 | * @param string $serviceId Name of the private service to create |
||
473 | * @param string $uri String representation of the URI |
||
474 | */ |
||
475 | 6 | private function createUri(ContainerBuilder $container, $serviceId, $uri) |
|
484 | |||
485 | /** |
||
486 | * Make the user can select what client is used for auto discovery. If none is provided, a service will be created |
||
487 | * by finding a client using auto discovery. |
||
488 | */ |
||
489 | 37 | private function configureAutoDiscoveryClients(ContainerBuilder $container, array $config) |
|
519 | |||
520 | /** |
||
521 | * {@inheritdoc} |
||
522 | */ |
||
523 | 37 | public function getConfiguration(array $config, ContainerBuilder $container) |
|
527 | |||
528 | /** |
||
529 | * Configure a plugin using the parent definition from plugins.xml. |
||
530 | * |
||
531 | * @param string $serviceId |
||
532 | * @param string $pluginName |
||
533 | * |
||
534 | * @return string configured service id |
||
535 | */ |
||
536 | 7 | private function configurePlugin(ContainerBuilder $container, $serviceId, $pluginName, array $pluginConfig) |
|
547 | |||
548 | 5 | private function configureVcrPlugin(ContainerBuilder $container, array $config, $prefix) |
|
603 | |||
604 | /** |
||
605 | * BC for old Symfony versions. Remove this method and use new ChildDefinition directly when we drop support for Symfony 2. |
||
606 | * |
||
607 | * @param string $parent the parent service id |
||
608 | * |
||
609 | * @return ChildDefinition|DefinitionDecorator |
||
610 | */ |
||
611 | 9 | private function createChildDefinition($parent) |
|
617 | } |
||
618 |