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 MathielenImportEngineExtension 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 MathielenImportEngineExtension, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
13 | class MathielenImportEngineExtension extends Extension |
||
14 | { |
||
15 | |||
16 | 11 | public function load(array $configs, ContainerBuilder $container) |
|
17 | { |
||
18 | 11 | $config = $this->processConfiguration(new Configuration(), $configs); |
|
19 | |||
20 | 11 | if (!empty($config['importers'])) { |
|
21 | 9 | $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); |
|
22 | 9 | $loader->load('services.xml'); |
|
23 | |||
24 | 9 | $this->parseConfig($config, $container); |
|
25 | } |
||
26 | 11 | } |
|
27 | |||
28 | 9 | private function parseConfig(array $config, ContainerBuilder $container) |
|
29 | { |
||
30 | 9 | $storageLocatorDef = $container->findDefinition('mathielen_importengine.import.storagelocator'); |
|
31 | 9 | foreach ($config['storageprovider'] as $name => $sourceConfig) { |
|
32 | 5 | $this->addStorageProviderDef($storageLocatorDef, $sourceConfig, $name); |
|
33 | } |
||
34 | |||
35 | 9 | $importerRepositoryDef = $container->findDefinition('mathielen_importengine.importer.repository'); |
|
36 | 9 | foreach ($config['importers'] as $name => $importConfig) { |
|
37 | 9 | $finderDef = null; |
|
38 | 9 | if (isset($importConfig['preconditions'])) { |
|
39 | 5 | $finderDef = $this->generateFinderDef($importConfig['preconditions']); |
|
40 | } |
||
41 | |||
42 | 9 | $objectFactoryDef = null; |
|
43 | 9 | if (isset($importConfig['object_factory'])) { |
|
44 | 5 | $objectFactoryDef = $this->generateObjectFactoryDef($importConfig['object_factory']); |
|
45 | } |
||
46 | |||
47 | 9 | $importerRepositoryDef->addMethodCall('register', array( |
|
48 | 9 | $name, |
|
49 | 9 | $this->generateImporterDef($importConfig, $objectFactoryDef), |
|
50 | 9 | $finderDef |
|
51 | )); |
||
52 | } |
||
53 | 9 | } |
|
54 | |||
55 | 5 | private function generateObjectFactoryDef(array $config) |
|
56 | { |
||
57 | 5 | if (!class_exists($config['class'])) { |
|
58 | throw new InvalidConfigurationException("Object-Factory target-class '".$config['class']."' does not exist."); |
||
59 | } |
||
60 | |||
61 | 5 | if ($config['type'] == 'jms_serializer') { |
|
62 | 5 | return new Definition('Mathielen\DataImport\Writer\ObjectWriter\JmsSerializerObjectFactory', array( |
|
63 | 5 | $config['class'], |
|
64 | 5 | new Reference('jms_serializer'))); |
|
65 | } |
||
66 | |||
67 | 5 | return new Definition('Mathielen\DataImport\Writer\ObjectWriter\DefaultObjectFactory', array($config['class'])); |
|
68 | } |
||
69 | |||
70 | /** |
||
71 | * @return \Symfony\Component\DependencyInjection\Definition |
||
72 | */ |
||
73 | 5 | private function generateFinderDef(array $finderConfig) |
|
74 | { |
||
75 | 5 | $finderDef = new Definition('Mathielen\ImportEngine\Importer\ImporterPrecondition'); |
|
76 | |||
77 | 5 | if (isset($finderConfig['filename'])) { |
|
78 | 5 | foreach ($finderConfig['filename'] as $conf) { |
|
79 | 5 | $finderDef->addMethodCall('filename', array($conf)); |
|
80 | } |
||
81 | } |
||
82 | |||
83 | 5 | View Code Duplication | if (isset($finderConfig['format'])) { |
|
|||
84 | 5 | foreach ($finderConfig['format'] as $conf) { |
|
85 | 5 | $finderDef->addMethodCall('format', array($conf)); |
|
86 | } |
||
87 | } |
||
88 | |||
89 | 5 | if (isset($finderConfig['fieldcount'])) { |
|
90 | 5 | $finderDef->addMethodCall('fieldcount', array($finderConfig['fieldcount'])); |
|
91 | } |
||
92 | |||
93 | 5 | View Code Duplication | if (isset($finderConfig['fields'])) { |
94 | 5 | foreach ($finderConfig['fields'] as $conf) { |
|
95 | 5 | $finderDef->addMethodCall('field', array($conf)); |
|
96 | } |
||
97 | } |
||
98 | |||
99 | 5 | if (isset($finderConfig['fieldset'])) { |
|
100 | 5 | $finderDef->addMethodCall('fieldset', array($finderConfig['fieldset'])); |
|
101 | } |
||
102 | |||
103 | 5 | return $finderDef; |
|
104 | } |
||
105 | |||
106 | /** |
||
107 | * @return \Symfony\Component\DependencyInjection\Definition |
||
108 | */ |
||
109 | 9 | private function generateImporterDef(array $importConfig, Definition $objectFactoryDef=null) |
|
110 | { |
||
111 | 9 | $importerDef = new Definition('Mathielen\ImportEngine\Importer\Importer', array( |
|
112 | 9 | $this->getStorageDef($importConfig['target'], $objectFactoryDef) |
|
113 | )); |
||
114 | |||
115 | 9 | if (isset($importConfig['source'])) { |
|
116 | 5 | $this->setSourceStorageDef($importConfig['source'], $importerDef); |
|
117 | } |
||
118 | |||
119 | //enable validation? |
||
120 | 9 | if (isset($importConfig['validation']) && !empty($importConfig['validation'])) { |
|
121 | 7 | $this->generateValidationDef($importConfig['validation'], $importerDef, $objectFactoryDef); |
|
122 | } |
||
123 | |||
124 | //add converters? |
||
125 | 9 | if (isset($importConfig['mappings']) && !empty($importConfig['mappings'])) { |
|
126 | 5 | $this->generateTransformerDef($importConfig['mappings'], $importerDef); |
|
127 | } |
||
128 | |||
129 | //add filters? |
||
130 | 9 | if (isset($importConfig['filters']) && !empty($importConfig['filters'])) { |
|
131 | $this->generateFiltersDef($importConfig['filters'], $importerDef); |
||
132 | } |
||
133 | |||
134 | //has static context? |
||
135 | 9 | if (isset($importConfig['context'])) { |
|
136 | 9 | $importerDef->addMethodCall('setContext', array( |
|
137 | 9 | $importConfig['context'] |
|
138 | )); |
||
139 | } |
||
140 | |||
141 | 9 | return $importerDef; |
|
142 | } |
||
143 | |||
144 | private function generateFiltersDef(array $filtersOptions, Definition $importerDef) |
||
145 | { |
||
146 | $filtersDef = new Definition('Mathielen\ImportEngine\Filter\Filters'); |
||
147 | |||
148 | foreach ($filtersOptions as $filtersOption) { |
||
149 | $filtersDef->addMethodCall('add', array( |
||
150 | new Reference($filtersOption) |
||
151 | )); |
||
152 | } |
||
153 | |||
154 | $importerDef->addMethodCall('filters', array( |
||
155 | $filtersDef |
||
156 | )); |
||
157 | } |
||
158 | |||
159 | 5 | private function generateTransformerDef(array $mappingOptions, Definition $importerDef) |
|
160 | { |
||
161 | 5 | $mappingsDef = new Definition('Mathielen\ImportEngine\Mapping\Mappings'); |
|
162 | |||
163 | //set converters |
||
164 | 5 | foreach ($mappingOptions as $field=>$fieldMapping) { |
|
165 | 5 | $converter = null; |
|
166 | 5 | if (isset($fieldMapping['converter'])) { |
|
167 | 5 | $converter = $fieldMapping['converter']; |
|
168 | } |
||
169 | |||
170 | 5 | if (isset($fieldMapping['to'])) { |
|
171 | 5 | $mappingsDef->addMethodCall('add', array( |
|
172 | 5 | $field, |
|
173 | 5 | $fieldMapping['to'], |
|
174 | 5 | $converter |
|
175 | )); |
||
176 | 5 | } elseif ($converter) { |
|
177 | 5 | $mappingsDef->addMethodCall('setConverter', array( |
|
178 | 5 | $converter, |
|
179 | 5 | $field |
|
180 | )); |
||
181 | } |
||
182 | } |
||
183 | |||
184 | 5 | $mappingFactoryDef = new Definition('Mathielen\ImportEngine\Mapping\DefaultMappingFactory', array( |
|
185 | 5 | $mappingsDef |
|
186 | )); |
||
187 | 5 | $converterProviderDef = new Definition('Mathielen\ImportEngine\Mapping\Converter\Provider\ContainerAwareConverterProvider', array( |
|
188 | 5 | new Reference('service_container') |
|
189 | )); |
||
190 | |||
191 | 5 | $transformerDef = new Definition('Mathielen\ImportEngine\Transformation\Transformation'); |
|
192 | 5 | $transformerDef->addMethodCall('setMappingFactory', array( |
|
193 | 5 | $mappingFactoryDef |
|
194 | )); |
||
195 | 5 | $transformerDef->addMethodCall('setConverterProvider', array( |
|
196 | 5 | $converterProviderDef |
|
197 | )); |
||
198 | |||
199 | 5 | $importerDef->addMethodCall('transformation', array( |
|
200 | 5 | $transformerDef |
|
201 | )); |
||
202 | 5 | } |
|
203 | |||
204 | 7 | private function generateValidatorDef(array $options) |
|
205 | { |
||
206 | //eventdispatcher aware source validatorfilter |
||
207 | 7 | $validatorFilterDef = new Definition('Mathielen\DataImport\Filter\ValidatorFilter', array( |
|
208 | 7 | new Reference('validator'), |
|
209 | 7 | $options, |
|
210 | 7 | new Reference('event_dispatcher') |
|
211 | )); |
||
212 | |||
213 | 7 | return $validatorFilterDef; |
|
214 | } |
||
215 | |||
216 | 7 | private function generateValidationDef(array $validationConfig, Definition $importerDef, Definition $objectFactoryDef=null) |
|
217 | { |
||
218 | 7 | $validationDef = new Definition('Mathielen\ImportEngine\Validation\ValidatorValidation', array( |
|
219 | 7 | new Reference('validator') |
|
220 | )); |
||
221 | 7 | $importerDef->addMethodCall('validation', array( |
|
222 | 7 | $validationDef |
|
223 | )); |
||
224 | |||
225 | 7 | $validatorFilterDef = $this->generateValidatorDef( |
|
226 | 7 | isset($validationConfig['options'])?$validationConfig['options']:array() |
|
227 | ); |
||
228 | |||
229 | 7 | if (isset($validationConfig['source'])) { |
|
230 | 3 | $validationDef->addMethodCall('setSourceValidatorFilter', array( |
|
231 | 3 | $validatorFilterDef |
|
232 | )); |
||
233 | |||
234 | 3 | foreach ($validationConfig['source']['constraints'] as $field=>$constraint) { |
|
235 | 3 | $validationDef->addMethodCall('addSourceConstraint', array( |
|
236 | 3 | $field, |
|
237 | 3 | new Definition($constraint) |
|
238 | )); |
||
239 | } |
||
240 | } |
||
241 | |||
242 | //automatically apply class validation |
||
243 | 7 | if (isset($validationConfig['target'])) { |
|
244 | |||
245 | //using objects as result |
||
246 | 7 | if ($objectFactoryDef) { |
|
247 | |||
248 | //set eventdispatcher aware target CLASS-validatorfilter |
||
249 | 5 | $validatorFilterDef = new Definition('Mathielen\DataImport\Filter\ClassValidatorFilter', array( |
|
250 | 5 | new Reference('validator'), |
|
251 | 5 | $objectFactoryDef, |
|
252 | 5 | new Reference('event_dispatcher') |
|
253 | )); |
||
254 | |||
255 | } else { |
||
256 | 3 | foreach ($validationConfig['target']['constraints'] as $field=>$constraint) { |
|
257 | 3 | $validationDef->addMethodCall('addTargetConstraint', array( |
|
258 | 3 | $field, |
|
259 | 3 | new Definition($constraint) |
|
260 | )); |
||
261 | } |
||
262 | } |
||
263 | |||
264 | 7 | $validationDef->addMethodCall('setTargetValidatorFilter', array( |
|
265 | 7 | $validatorFilterDef |
|
266 | )); |
||
267 | } |
||
268 | |||
269 | 7 | return $validationDef; |
|
270 | } |
||
271 | |||
272 | 5 | private function setSourceStorageDef(array $sourceConfig, Definition $importerDef) |
|
273 | { |
||
274 | 5 | $sDef = $this->getStorageDef($sourceConfig, $importerDef); |
|
275 | 5 | $importerDef->addMethodCall('setSourceStorage', array( |
|
276 | 5 | $sDef |
|
277 | )); |
||
278 | 5 | } |
|
279 | |||
280 | 5 | private function addStorageProviderDef(Definition $storageLocatorDef, $config, $id = 'default') |
|
281 | { |
||
282 | 5 | $formatDiscoverLocalFileStorageFactoryDef = new Definition('Mathielen\ImportEngine\Storage\Factory\FormatDiscoverLocalFileStorageFactory', array( |
|
283 | 5 | new Definition('Mathielen\ImportEngine\Storage\Format\Discovery\MimeTypeDiscoverStrategy', array( |
|
284 | array( |
||
285 | 5 | 'text/plain'=>new Definition('Mathielen\ImportEngine\Storage\Format\Factory\CsvAutoDelimiterFormatFactory'), |
|
286 | 5 | 'text/csv'=>new Definition('Mathielen\ImportEngine\Storage\Format\Factory\CsvAutoDelimiterFormatFactory'), |
|
287 | ) |
||
288 | )), |
||
289 | 5 | new Reference('logger') |
|
290 | )); |
||
291 | |||
292 | 5 | switch ($config['type']) { |
|
293 | case 'directory': |
||
294 | 5 | $spFinderDef = new Definition('Symfony\Component\Finder\Finder'); |
|
295 | 5 | $spFinderDef->addMethodCall('in', array( |
|
296 | 5 | $config['uri'] |
|
297 | )); |
||
298 | 5 | $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\FinderFileStorageProvider', array( |
|
299 | 5 | $spFinderDef, |
|
300 | 5 | $formatDiscoverLocalFileStorageFactoryDef |
|
301 | )); |
||
302 | 5 | break; |
|
303 | case 'upload': |
||
304 | 5 | $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\UploadFileStorageProvider', array( |
|
305 | 5 | $config['uri'], |
|
306 | 5 | $formatDiscoverLocalFileStorageFactoryDef |
|
307 | )); |
||
308 | 5 | break; |
|
309 | case 'doctrine': |
||
310 | 5 | $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\DoctrineQueryStorageProvider', array( |
|
311 | 5 | new Reference('doctrine.orm.entity_manager'), |
|
312 | 5 | $config['queries'] |
|
313 | )); |
||
314 | 5 | break; |
|
315 | case 'service': |
||
316 | 5 | $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\ServiceStorageProvider', array( |
|
317 | 5 | new Reference('service_container'), |
|
318 | 5 | $config['services'] |
|
319 | )); |
||
320 | 5 | break; |
|
321 | case 'file': |
||
322 | 5 | $spDef = new Definition('Mathielen\ImportEngine\Storage\Provider\FileStorageProvider', array( |
|
323 | 5 | $formatDiscoverLocalFileStorageFactoryDef |
|
324 | )); |
||
325 | 5 | break; |
|
326 | default: |
||
327 | throw new InvalidConfigurationException('Unknown type for storage provider: '.$config['type']); |
||
328 | } |
||
329 | |||
330 | 5 | $storageLocatorDef->addMethodCall('register', array( |
|
331 | 5 | $id, |
|
332 | 5 | $spDef |
|
333 | )); |
||
334 | 5 | } |
|
335 | |||
336 | 9 | private function getStorageFileDefinitionFromUri($uri) |
|
337 | { |
||
338 | 9 | if (substr($uri, 0, 2) === '@=') { |
|
339 | $uri = new Expression(substr($uri, 2)); |
||
340 | } |
||
341 | |||
342 | 9 | return new Definition('SplFileInfo', array( |
|
343 | 9 | $uri |
|
344 | )); |
||
345 | } |
||
346 | |||
347 | /** |
||
348 | * @return Definition |
||
349 | */ |
||
350 | 9 | private function getStorageDef(array $config, Definition $objectFactoryDef=null) |
|
351 | { |
||
352 | 9 | switch ($config['type']) { |
|
353 | case 'file': |
||
354 | 9 | $fileDef = $this->getStorageFileDefinitionFromUri($config['uri']); |
|
355 | |||
356 | 9 | $format = $config['format']; |
|
357 | 9 | $storageDef = new Definition('Mathielen\ImportEngine\Storage\LocalFileStorage', array( |
|
358 | 9 | $fileDef, |
|
359 | 9 | new Definition('Mathielen\ImportEngine\Storage\Format\\'.ucfirst($format['type'])."Format", $format['arguments']) |
|
360 | )); |
||
361 | |||
362 | 9 | break; |
|
363 | case 'doctrine': |
||
364 | 5 | $storageDef = new Definition('Mathielen\ImportEngine\Storage\DoctrineStorage', array( |
|
365 | 5 | new Reference('doctrine.orm.entity_manager'), |
|
366 | 5 | $config['entity'] |
|
367 | )); |
||
368 | |||
369 | 5 | break; |
|
370 | case 'service': |
||
371 | 5 | $storageDef = new Definition('Mathielen\ImportEngine\Storage\ServiceStorage', array( |
|
372 | 5 | array(new Reference($config['service']), $config['method']), //callable |
|
373 | array(), |
||
374 | 5 | $objectFactoryDef //from parameter array |
|
375 | )); |
||
376 | |||
377 | 5 | break; |
|
378 | default: |
||
379 | throw new InvalidConfigurationException('Unknown type for storage: '.$config['type']); |
||
380 | } |
||
381 | |||
382 | 9 | return $storageDef; |
|
383 | } |
||
384 | |||
385 | 11 | public function getAlias() |
|
389 | } |
||
390 |
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.