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 DocumentParser 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 DocumentParser, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
28 | class DocumentParser |
||
29 | { |
||
30 | const PROPERTY_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Property'; |
||
31 | const EMBEDDED_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Embedded'; |
||
32 | const DOCUMENT_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Document'; |
||
33 | const OBJECT_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Object'; |
||
34 | const NESTED_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Nested'; |
||
35 | |||
36 | // Meta fields |
||
37 | const ID_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Id'; |
||
38 | const PARENT_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\ParentDocument'; |
||
39 | const ROUTING_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Routing'; |
||
40 | const VERSION_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\Version'; |
||
41 | const HASH_MAP_ANNOTATION = 'ONGR\ElasticsearchBundle\Annotation\HashMap'; |
||
42 | |||
43 | /** |
||
44 | * @var Reader Used to read document annotations. |
||
45 | */ |
||
46 | private $reader; |
||
47 | |||
48 | /** |
||
49 | * @var DocumentFinder Used to find documents. |
||
50 | */ |
||
51 | private $finder; |
||
52 | |||
53 | /** |
||
54 | * @var array Contains gathered objects which later adds to documents. |
||
55 | */ |
||
56 | private $objects = []; |
||
57 | |||
58 | /** |
||
59 | * @var array Document properties aliases. |
||
60 | */ |
||
61 | private $aliases = []; |
||
62 | |||
63 | /** |
||
64 | * @var array Local cache for document properties. |
||
65 | */ |
||
66 | private $properties = []; |
||
67 | |||
68 | /** |
||
69 | * @param Reader $reader Used for reading annotations. |
||
70 | * @param DocumentFinder $finder Used for resolving namespaces. |
||
71 | */ |
||
72 | public function __construct(Reader $reader, DocumentFinder $finder) |
||
78 | |||
79 | /** |
||
80 | * Parses documents by used annotations and returns mapping for elasticsearch with some extra metadata. |
||
81 | * |
||
82 | * @param \ReflectionClass $class |
||
83 | * |
||
84 | * @return array |
||
85 | * @throws MissingDocumentAnnotationException |
||
86 | */ |
||
87 | public function parse(\ReflectionClass $class) |
||
119 | |||
120 | /** |
||
121 | * Returns document annotation data from reader. |
||
122 | * |
||
123 | * @param \ReflectionClass $document |
||
124 | * |
||
125 | * @return Document|object|null |
||
126 | */ |
||
127 | private function getDocumentAnnotationData($document) |
||
131 | |||
132 | /** |
||
133 | * Returns property annotation data from reader. |
||
134 | * |
||
135 | * @param \ReflectionProperty $property |
||
136 | * |
||
137 | * @return Property|object|null |
||
138 | */ |
||
139 | View Code Duplication | private function getPropertyAnnotationData(\ReflectionProperty $property) |
|
149 | |||
150 | /** |
||
151 | * Returns Embedded annotation data from reader. |
||
152 | * |
||
153 | * @param \ReflectionProperty $property |
||
154 | * |
||
155 | * @return Embedded|object|null |
||
156 | */ |
||
157 | View Code Duplication | private function getEmbeddedAnnotationData(\ReflectionProperty $property) |
|
167 | |||
168 | /** |
||
169 | * Returns HashMap annotation data from reader. |
||
170 | * |
||
171 | * @param \ReflectionProperty $property |
||
172 | * |
||
173 | * @return HashMap|object|null |
||
174 | */ |
||
175 | View Code Duplication | private function getHashMapAnnotationData(\ReflectionProperty $property) |
|
185 | |||
186 | /** |
||
187 | * Returns meta field annotation data from reader. |
||
188 | * |
||
189 | * @param \ReflectionProperty $property |
||
190 | * @param string $directory The name of the Document directory in the bundle |
||
191 | * |
||
192 | * @return array |
||
193 | */ |
||
194 | private function getMetaFieldAnnotationData($property, $directory) |
||
217 | |||
218 | /** |
||
219 | * Returns objects used in document. |
||
220 | * |
||
221 | * @return array |
||
222 | */ |
||
223 | private function getObjects() |
||
227 | |||
228 | /** |
||
229 | * Finds aliases for every property used in document including parent classes. |
||
230 | * |
||
231 | * @param \ReflectionClass $reflectionClass |
||
232 | * @param array $metaFields |
||
233 | * |
||
234 | * @return array |
||
235 | */ |
||
236 | private function getAliases(\ReflectionClass $reflectionClass, array &$metaFields = null) |
||
322 | |||
323 | /** |
||
324 | * Checks if class have setter and getter, and returns them in array. |
||
325 | * |
||
326 | * @param \ReflectionClass $reflectionClass |
||
327 | * @param string $property |
||
328 | * |
||
329 | * @return array |
||
330 | */ |
||
331 | private function getMutatorMethods(\ReflectionClass $reflectionClass, $property, $propertyType) |
||
375 | |||
376 | /** |
||
377 | * Registers annotations to registry so that it could be used by reader. |
||
378 | */ |
||
379 | private function registerAnnotations() |
||
398 | |||
399 | /** |
||
400 | * Returns document type. |
||
401 | * |
||
402 | * @param string $document Format must be like AcmeBundle:Document. |
||
403 | * @param string $directory The Document directory name of the bundle. |
||
404 | * |
||
405 | * @return string |
||
406 | */ |
||
407 | private function getDocumentType($document, $directory) |
||
415 | |||
416 | /** |
||
417 | * Returns all defined properties including private from parents. |
||
418 | * |
||
419 | * @param \ReflectionClass $reflectionClass |
||
420 | * |
||
421 | * @return array |
||
422 | */ |
||
423 | private function getDocumentPropertiesReflection(\ReflectionClass $reflectionClass) |
||
449 | |||
450 | /** |
||
451 | * Parses analyzers list from document mapping. |
||
452 | * |
||
453 | * @param \ReflectionClass $reflectionClass |
||
454 | * @return array |
||
455 | */ |
||
456 | private function getAnalyzers(\ReflectionClass $reflectionClass) |
||
494 | |||
495 | /** |
||
496 | * Returns properties of reflection class. |
||
497 | * |
||
498 | * @param \ReflectionClass $reflectionClass Class to read properties from. |
||
499 | * @param array $properties Properties to skip. |
||
500 | * @param bool $flag If false exludes properties, true only includes properties. |
||
501 | * |
||
502 | * @return array |
||
503 | */ |
||
504 | private function getProperties(\ReflectionClass $reflectionClass, $properties = [], $flag = false) |
||
549 | |||
550 | /** |
||
551 | * Returns object mapping. |
||
552 | * |
||
553 | * Loads from cache if it's already loaded. |
||
554 | * |
||
555 | * @param string $className |
||
556 | * @param string $directory Name of the directory where the Document is |
||
557 | * |
||
558 | * @return array |
||
559 | */ |
||
560 | private function getObjectMapping($className, $directory) |
||
593 | |||
594 | /** |
||
595 | * @param \ReflectionClass $reflection |
||
596 | * |
||
597 | * @return string |
||
598 | */ |
||
599 | private function guessDirName(\ReflectionClass $reflection) |
||
607 | } |
||
608 |
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.