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.