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 ImportCsvConnector 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 ImportCsvConnector, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
33 | class ImportCsvConnector extends ImportConnector { |
||
34 | |||
35 | /** |
||
36 | * @brief separates elements from the input stream according to the entry_separator value in config |
||
37 | * ignoring the first line if mentionned in the config |
||
38 | * @param $file the input file to import |
||
39 | * @param $limit the number of elements to return (-1 = no limit) |
||
40 | * @return array(array(data), array(titles)) |
||
|
|||
41 | */ |
||
42 | public function getElementsFromInput($file, $limit=-1) { |
||
43 | |||
44 | $linesAndTitles = $this->getSourceElementsFromFile($file, $limit); |
||
45 | $lines = $linesAndTitles[0]; |
||
46 | $titles = $linesAndTitles[1]; |
||
47 | $elements = array(); |
||
48 | foreach ($lines as $line) { |
||
49 | $elements[] = $this->convertElementToVCard($line, $titles); |
||
50 | } |
||
51 | |||
52 | return array_values($elements); |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * @brief parses the file in csv format |
||
57 | * @param $file the input file to import |
||
58 | * @param $limit the number of elements to return (-1 = no limit) |
||
59 | * @return array(array(data), array(titles)) |
||
60 | */ |
||
61 | private function getSourceElementsFromFile($file, $limit=-1) { |
||
62 | if (file_put_contents($file, StringUtil::convertToUTF8(file_get_contents($file)))) { |
||
63 | $csv = new SplFileObject($file, 'r'); |
||
64 | $csv->setFlags(SplFileObject::READ_CSV); |
||
65 | |||
66 | $delimiter = ''; |
||
67 | if (isset($this->configContent->import_core->delimiter)) { |
||
68 | $delimiter = (string)$this->configContent->import_core->delimiter; |
||
69 | } else { |
||
70 | // Look for the delimiter in the first line, should be the most present character between ',', ';' and '\t' |
||
71 | $splFile = new SplFileObject($file); |
||
72 | $firstLine = $splFile->fgets(); |
||
73 | $nbComma = substr_count($firstLine, ','); |
||
74 | $nbSemicolon = substr_count($firstLine, ';'); |
||
75 | $nbTab = substr_count($firstLine, "\t"); |
||
76 | if ($nbComma > $nbSemicolon && $nbComma > $nbTab) { |
||
77 | // Comma it is |
||
78 | $delimiter = ','; |
||
79 | } else if ($nbSemicolon > $nbComma && $nbSemicolon > $nbTab) { |
||
80 | // Semicolon it is |
||
81 | $delimiter = ';'; |
||
82 | } else if ($nbTab > $nbComma && $nbTab > $nbSemicolon) { |
||
83 | // Tab it is |
||
84 | $delimiter = "\t"; |
||
85 | } else if ($nbTab == 0 && $nbComma == 0 && $nbSemicolon == 0) { |
||
86 | // We have a problem, no delimiter found |
||
87 | return array(array(), array()); |
||
88 | } |
||
89 | } |
||
90 | $csv->setCsvControl($delimiter, "\"", "\\"); |
||
91 | |||
92 | $ignoreFirstLine = (isset($this->configContent->import_core->ignore_first_line) |
||
93 | && (((string)$this->configContent->import_core->ignore_first_line) == 'true') |
||
94 | || ((string)$this->configContent->import_core->ignore_first_line) == '1'); |
||
95 | |||
96 | $titles = false; |
||
97 | |||
98 | $lines = array(); |
||
99 | |||
100 | $index = 0; |
||
101 | foreach($csv as $line) { |
||
102 | if (!($ignoreFirstLine && $index == 0) && count($line) > 1) { // Ignore first line |
||
103 | |||
104 | $lines[] = $line; |
||
105 | |||
106 | if (count($lines) == $limit) { |
||
107 | break; |
||
108 | } |
||
109 | } else if ($ignoreFirstLine && $index == 0) { |
||
110 | $titles = $line; |
||
111 | } |
||
112 | $index++; |
||
113 | } |
||
114 | |||
115 | return array($lines, $titles); |
||
116 | } else { |
||
117 | error_log("Error converting file to utf8"); |
||
118 | return array(array(), array()); |
||
119 | } |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * @brief converts a unique element into a owncloud VCard |
||
124 | * @param $element the element to convert |
||
125 | * @return VCard, all unconverted elements are stored in X-Unknown-Element parameters |
||
126 | */ |
||
127 | public function convertElementToVCard($element, $title = null) { |
||
128 | $vcard = new \OCA\Contacts\VObject\VCard(); |
||
129 | |||
130 | $nbElt = count($element); |
||
131 | for ($i=0; $i < $nbElt; $i++) { |
||
132 | if ($element[$i] != '') { |
||
133 | //$importEntry = false; |
||
134 | // Look for the right import_entry |
||
135 | if (isset($this->configContent->import_core->base_parsing)) { |
||
136 | if (strcasecmp((string)$this->configContent->import_core->base_parsing, 'position') == 0) { |
||
137 | $importEntry = $this->getImportEntryFromPosition((String)$i); |
||
138 | } else if (strcasecmp((string)$this->configContent->import_core->base_parsing, 'name') == 0 && isset($title[$i])) { |
||
139 | $importEntry = $this->getImportEntryFromName($title[$i]); |
||
140 | } |
||
141 | } |
||
142 | if ($importEntry) { |
||
143 | // Create a new property and attach it to the vcard |
||
144 | $value = $element[$i]; |
||
145 | if (isset($importEntry['remove'])) { |
||
146 | $value = str_replace($importEntry['remove'], '', $element[$i]); |
||
147 | } |
||
148 | $values = array($value); |
||
149 | if (isset($importEntry['separator'])) { |
||
150 | $values = explode($importEntry['separator'], $value); |
||
151 | } |
||
152 | |||
153 | View Code Duplication | foreach ($values as $oneValue) { |
|
154 | if (isset($importEntry->vcard_favourites)) { |
||
155 | foreach ($importEntry->vcard_favourites as $vcardFavourite) { |
||
156 | if (strcasecmp((string)$vcardFavourite, trim($oneValue)) == 0) { |
||
157 | $property = $vcard->createProperty("X-FAVOURITES", 'yes'); |
||
158 | $vcard->add($property); |
||
159 | } else { |
||
160 | $property = $this->getOrCreateVCardProperty($vcard, $importEntry->vcard_entry); |
||
161 | $this->updateProperty($property, $importEntry, trim($oneValue)); |
||
162 | } |
||
163 | } |
||
164 | } else { |
||
165 | $property = $this->getOrCreateVCardProperty($vcard, $importEntry->vcard_entry); |
||
166 | $this->updateProperty($property, $importEntry, trim($oneValue)); |
||
167 | } |
||
168 | } |
||
169 | } else if (isset($element[$i]) && isset($title[$i])) { |
||
170 | $property = $vcard->createProperty("X-Unknown-Element", StringUtil::convertToUTF8($element[$i])); |
||
171 | $property->add('TYPE', StringUtil::convertToUTF8($title[$i])); |
||
172 | $vcard->add($property); |
||
173 | } |
||
174 | } |
||
175 | } |
||
176 | $vcard->validate(\Sabre\VObject\Component\VCard::REPAIR); |
||
177 | return $vcard; |
||
178 | } |
||
179 | |||
180 | /** |
||
181 | * @brief gets the import entry corresponding to the position given in parameter |
||
182 | * @param string $position the position to look for in the connector |
||
183 | * @return int|false |
||
184 | */ |
||
185 | private function getImportEntryFromPosition($position) { |
||
194 | |||
195 | /** |
||
196 | * @brief gets the import entry corresponding to the name given in parameter |
||
197 | * @param $name the parameter name to look for in the connector |
||
198 | * @return string|false |
||
199 | */ |
||
200 | private function getImportEntryFromName($name) { |
||
216 | |||
217 | /** |
||
218 | * @brief returns the probability that the first element is a match for this format |
||
219 | * @param $file the file to examine |
||
220 | * @return 0 if not a valid csv file |
||
221 | * 1 - 0.5*(number of untranslated elements/total number of elements) |
||
222 | * The more the first element has untranslated elements, the more the result is close to 0.5 |
||
223 | */ |
||
224 | public function getFormatMatch($file) { |
||
245 | } |
||
246 | |||
248 |
This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.