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:
| 1 | <?php |
||
| 40 | class BazingaGeocoderExtension extends Extension |
||
| 41 | { |
||
| 42 | 31 | public function load(array $configs, ContainerBuilder $container) |
|
| 43 | { |
||
| 44 | 31 | $processor = new Processor(); |
|
| 45 | 31 | $configuration = $this->getConfiguration($configs, $container); |
|
| 46 | 31 | $config = $processor->processConfiguration($configuration, $configs); |
|
|
|
|||
| 47 | |||
| 48 | 31 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); |
|
| 49 | 31 | $loader->load('services.yml'); |
|
| 50 | |||
| 51 | 31 | if (true === $config['profiling']['enabled']) { |
|
| 52 | 3 | $loader->load('profiling.yml'); |
|
| 53 | } |
||
| 54 | |||
| 55 | 31 | if ($config['fake_ip']['enabled']) { |
|
| 56 | $definition = $container->getDefinition(FakeIpPlugin::class); |
||
| 57 | $definition->replaceArgument(1, $config['fake_ip']['ip']); |
||
| 58 | } else { |
||
| 59 | 31 | $container->removeDefinition(FakeIpPlugin::class); |
|
| 60 | } |
||
| 61 | |||
| 62 | 31 | $this->loadProviders($container, $config); |
|
| 63 | |||
| 64 | 31 | $container->registerForAutoconfiguration(Dumper::class) |
|
| 65 | 31 | ->addTag('bazinga_geocoder.dumper'); |
|
| 66 | 31 | } |
|
| 67 | |||
| 68 | 31 | private function loadProviders(ContainerBuilder $container, array $config) |
|
| 69 | { |
||
| 70 | 31 | foreach ($config['providers'] as $providerName => $providerConfig) { |
|
| 71 | try { |
||
| 72 | 29 | $factoryService = $container->getDefinition($providerConfig['factory']); |
|
| 73 | 29 | $factoryClass = $factoryService->getClass() ?: $providerConfig['factory']; |
|
| 74 | 29 | if (!$this->implementsProviderFactory($factoryClass)) { |
|
| 75 | throw new \LogicException(sprintf('Provider factory "%s" must implement ProviderFactoryInterface', $providerConfig['factory'])); |
||
| 76 | } |
||
| 77 | // See if any option has a service reference |
||
| 78 | 29 | $providerConfig['options'] = $this->findReferences($providerConfig['options']); |
|
| 79 | 29 | $factoryClass::validate($providerConfig['options'], $providerName); |
|
| 80 | } catch (ServiceNotFoundException $e) { |
||
| 81 | // Assert: We are using a custom factory. If invalid config, it will be caught in FactoryValidatorPass |
||
| 82 | $providerConfig['options'] = $this->findReferences($providerConfig['options']); |
||
| 83 | FactoryValidatorPass::addFactoryServiceId($providerConfig['factory']); |
||
| 84 | } |
||
| 85 | |||
| 86 | 29 | $serviceId = 'bazinga_geocoder.provider.'.$providerName; |
|
| 87 | 29 | $plugins = $this->configureProviderPlugins($container, $providerConfig, $serviceId); |
|
| 88 | |||
| 89 | 29 | $def = $container->register($serviceId, PluginProvider::class) |
|
| 90 | 29 | ->setFactory([PluginProviderFactory::class, 'createPluginProvider']) |
|
| 91 | 29 | ->addArgument($plugins) |
|
| 92 | 29 | ->addArgument(new Reference($providerConfig['factory'])) |
|
| 93 | 29 | ->addArgument($providerConfig['options']); |
|
| 94 | |||
| 95 | 29 | $def->addTag('bazinga_geocoder.provider'); |
|
| 96 | 29 | foreach ($providerConfig['aliases'] as $alias) { |
|
| 97 | $container->setAlias($alias, $serviceId); |
||
| 98 | } |
||
| 99 | |||
| 100 | 29 | if (Kernel::VERSION_ID > 40200) { |
|
| 101 | 29 | $container->registerAliasForArgument($serviceId, Provider::class, "{$providerName}Geocoder"); |
|
| 102 | } |
||
| 103 | } |
||
| 104 | 31 | } |
|
| 105 | |||
| 106 | /** |
||
| 107 | * Configure plugins for a client. |
||
| 108 | */ |
||
| 109 | 29 | public function configureProviderPlugins(ContainerBuilder $container, array $config, string $providerServiceId): array |
|
| 110 | { |
||
| 111 | 29 | $plugins = []; |
|
| 112 | 29 | foreach ($config['plugins'] as $plugin) { |
|
| 113 | 2 | if ($plugin['reference']['enabled']) { |
|
| 114 | 2 | $plugins[] = $plugin['reference']['id']; |
|
| 115 | } |
||
| 116 | } |
||
| 117 | |||
| 118 | 29 | if (isset($config['cache']) || isset($config['cache_lifetime']) || isset($config['cache_precision'])) { |
|
| 119 | 2 | $cacheLifetime = isset($config['cache_lifetime']) ? (int) $config['cache_lifetime'] : null; |
|
| 120 | |||
| 121 | 2 | if (null === $cacheServiceId = $config['cache']) { |
|
| 122 | if (!$container->has('app.cache')) { |
||
| 123 | throw new \LogicException('You need to specify a service for cache.'); |
||
| 124 | } |
||
| 125 | $cacheServiceId = 'app.cache'; |
||
| 126 | } |
||
| 127 | 2 | $plugins[] = $providerServiceId.'.cache'; |
|
| 128 | 2 | $container->register($providerServiceId.'.cache', CachePlugin::class) |
|
| 129 | 2 | ->setPublic(false) |
|
| 130 | 2 | ->setArguments([new Reference($cacheServiceId), $cacheLifetime, $config['cache_precision']]); |
|
| 131 | } |
||
| 132 | |||
| 133 | 29 | View Code Duplication | if (isset($config['limit'])) { |
| 134 | $plugins[] = $providerServiceId.'.limit'; |
||
| 135 | $container->register($providerServiceId.'.limit', LimitPlugin::class) |
||
| 136 | ->setPublic(false) |
||
| 137 | ->setArguments([(int) $config['limit']]); |
||
| 138 | } |
||
| 139 | |||
| 140 | 29 | View Code Duplication | if (isset($config['locale'])) { |
| 141 | $plugins[] = $providerServiceId.'.locale'; |
||
| 142 | $container->register($providerServiceId.'.locale', LocalePlugin::class) |
||
| 143 | ->setPublic(false) |
||
| 144 | ->setArguments([$config['locale']]); |
||
| 145 | } |
||
| 146 | |||
| 147 | 29 | View Code Duplication | if (isset($config['logger'])) { |
| 148 | $plugins[] = $providerServiceId.'.logger'; |
||
| 149 | $container->register($providerServiceId.'.logger', LoggerPlugin::class) |
||
| 150 | ->setPublic(false) |
||
| 151 | ->setArguments([new Reference($config['logger'])]); |
||
| 152 | } |
||
| 153 | |||
| 154 | 29 | if ($container->has(FakeIpPlugin::class)) { |
|
| 155 | $plugins[] = FakeIpPlugin::class; |
||
| 156 | } |
||
| 157 | |||
| 158 | 29 | if ($container->has(GeocoderDataCollector::class)) { |
|
| 159 | 1 | $plugins[] = $providerServiceId.'.profiler'; |
|
| 160 | 1 | $container->register($providerServiceId.'.profiler', ProfilingPlugin::class) |
|
| 161 | 1 | ->setPublic(false) |
|
| 162 | 1 | ->setArguments([substr($providerServiceId, strlen('bazinga_geocoder.provider.'))]) |
|
| 163 | 1 | ->addTag('bazinga_geocoder.profiling_plugin'); |
|
| 164 | } |
||
| 165 | |||
| 166 | return array_map(function (string $id) { |
||
| 167 | 4 | return new Reference($id); |
|
| 168 | 29 | }, $plugins); |
|
| 169 | } |
||
| 170 | |||
| 171 | /** |
||
| 172 | * {@inheritdoc} |
||
| 173 | */ |
||
| 174 | 31 | public function getConfiguration(array $config, ContainerBuilder $container) |
|
| 178 | |||
| 179 | 29 | private function findReferences(array $options): array |
|
| 180 | { |
||
| 181 | 29 | foreach ($options as $key => $value) { |
|
| 191 | |||
| 192 | /** |
||
| 193 | * @param mixed $factoryClass |
||
| 194 | */ |
||
| 195 | 29 | private function implementsProviderFactory($factoryClass): bool |
|
| 203 | } |
||
| 204 |
Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code: