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 Csv 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 Csv, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
29 | class Csv extends BaseReader implements IReader |
||
30 | { |
||
31 | /** |
||
32 | * Input encoding. |
||
33 | * |
||
34 | * @var string |
||
35 | */ |
||
36 | private $inputEncoding = 'UTF-8'; |
||
37 | |||
38 | /** |
||
39 | * Delimiter. |
||
40 | * |
||
41 | * @var string |
||
42 | */ |
||
43 | private $delimiter = null; |
||
44 | |||
45 | /** |
||
46 | * Enclosure. |
||
47 | * |
||
48 | * @var string |
||
49 | */ |
||
50 | private $enclosure = '"'; |
||
51 | |||
52 | /** |
||
53 | * Sheet index to read. |
||
54 | * |
||
55 | * @var int |
||
56 | */ |
||
57 | private $sheetIndex = 0; |
||
58 | |||
59 | /** |
||
60 | * Load rows contiguously. |
||
61 | * |
||
62 | * @var bool |
||
63 | */ |
||
64 | private $contiguous = false; |
||
65 | |||
66 | /** |
||
67 | * Row counter for loading rows contiguously. |
||
68 | * |
||
69 | * @var int |
||
70 | */ |
||
71 | private $contiguousRow = -1; |
||
72 | |||
73 | /** |
||
74 | * Create a new CSV Reader instance. |
||
75 | */ |
||
76 | 3 | public function __construct() |
|
80 | |||
81 | /** |
||
82 | * Set input encoding. |
||
83 | * |
||
84 | * @param string $pValue Input encoding, eg: 'UTF-8' |
||
85 | */ |
||
86 | public function setInputEncoding($pValue) |
||
92 | |||
93 | /** |
||
94 | * Get input encoding. |
||
95 | * |
||
96 | * @return string |
||
97 | */ |
||
98 | public function getInputEncoding() |
||
102 | |||
103 | /** |
||
104 | * Move filepointer past any BOM marker. |
||
105 | */ |
||
106 | 3 | protected function skipBOM() |
|
107 | { |
||
108 | 3 | rewind($this->fileHandle); |
|
109 | |||
110 | 3 | switch ($this->inputEncoding) { |
|
111 | 3 | case 'UTF-8': |
|
112 | 3 | fgets($this->fileHandle, 4) == "\xEF\xBB\xBF" ? |
|
113 | 3 | fseek($this->fileHandle, 3) : fseek($this->fileHandle, 0); |
|
114 | 3 | break; |
|
115 | View Code Duplication | case 'UTF-16LE': |
|
|
|||
116 | fgets($this->fileHandle, 3) == "\xFF\xFE" ? |
||
117 | fseek($this->fileHandle, 2) : fseek($this->fileHandle, 0); |
||
118 | break; |
||
119 | View Code Duplication | case 'UTF-16BE': |
|
120 | fgets($this->fileHandle, 3) == "\xFE\xFF" ? |
||
121 | fseek($this->fileHandle, 2) : fseek($this->fileHandle, 0); |
||
122 | break; |
||
123 | View Code Duplication | case 'UTF-32LE': |
|
124 | fgets($this->fileHandle, 5) == "\xFF\xFE\x00\x00" ? |
||
125 | fseek($this->fileHandle, 4) : fseek($this->fileHandle, 0); |
||
126 | break; |
||
127 | View Code Duplication | case 'UTF-32BE': |
|
128 | fgets($this->fileHandle, 5) == "\x00\x00\xFE\xFF" ? |
||
129 | fseek($this->fileHandle, 4) : fseek($this->fileHandle, 0); |
||
130 | break; |
||
131 | default: |
||
132 | break; |
||
133 | } |
||
134 | 3 | } |
|
135 | |||
136 | /** |
||
137 | * Identify any separator that is explicitly set in the file. |
||
138 | */ |
||
139 | 3 | protected function checkSeparator() |
|
154 | |||
155 | /** |
||
156 | * Infer the separator if it isn't explicitly set in the file or specified by the user. |
||
157 | */ |
||
158 | 3 | protected function inferSeparator() |
|
234 | |||
235 | /** |
||
236 | * Return worksheet info (Name, Last Column Letter, Last Column Index, Total Rows, Total Columns). |
||
237 | * |
||
238 | * @param string $pFilename |
||
239 | * |
||
240 | * @throws Exception |
||
241 | */ |
||
242 | public function listWorksheetInfo($pFilename) |
||
277 | |||
278 | /** |
||
279 | * Loads Spreadsheet from file. |
||
280 | * |
||
281 | * @param string $pFilename |
||
282 | * |
||
283 | * @throws Exception |
||
284 | * |
||
285 | * @return \PhpOffice\PhpSpreadsheet\Spreadsheet |
||
286 | */ |
||
287 | 3 | public function load($pFilename) |
|
295 | |||
296 | /** |
||
297 | * Loads PhpSpreadsheet from file into PhpSpreadsheet instance. |
||
298 | * |
||
299 | * @param string $pFilename |
||
300 | * @param Spreadsheet $spreadsheet |
||
301 | * |
||
302 | * @throws Exception |
||
303 | * |
||
304 | * @return Spreadsheet |
||
305 | */ |
||
306 | 3 | public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet) |
|
365 | |||
366 | /** |
||
367 | * Get delimiter. |
||
368 | * |
||
369 | * @return string |
||
370 | */ |
||
371 | 1 | public function getDelimiter() |
|
372 | { |
||
373 | 1 | return $this->delimiter; |
|
374 | } |
||
375 | |||
376 | /** |
||
377 | * Set delimiter. |
||
378 | * |
||
379 | * @param string $delimiter Delimiter, eg: ',' |
||
380 | * |
||
381 | * @return CSV |
||
382 | */ |
||
383 | 1 | public function setDelimiter($delimiter) |
|
389 | |||
390 | /** |
||
391 | * Get enclosure. |
||
392 | * |
||
393 | * @return string |
||
394 | */ |
||
395 | public function getEnclosure() |
||
399 | |||
400 | /** |
||
401 | * Set enclosure. |
||
402 | * |
||
403 | * @param string $enclosure Enclosure, defaults to " |
||
404 | * |
||
405 | * @return CSV |
||
406 | */ |
||
407 | 1 | public function setEnclosure($enclosure) |
|
416 | |||
417 | /** |
||
418 | * Get sheet index. |
||
419 | * |
||
420 | * @return int |
||
421 | */ |
||
422 | public function getSheetIndex() |
||
426 | |||
427 | /** |
||
428 | * Set sheet index. |
||
429 | * |
||
430 | * @param int $pValue Sheet index |
||
431 | * |
||
432 | * @return CSV |
||
433 | */ |
||
434 | 1 | public function setSheetIndex($pValue) |
|
440 | |||
441 | /** |
||
442 | * Set Contiguous. |
||
443 | * |
||
444 | * @param bool $contiguous |
||
445 | */ |
||
446 | public function setContiguous($contiguous) |
||
455 | |||
456 | /** |
||
457 | * Get Contiguous. |
||
458 | * |
||
459 | * @return bool |
||
460 | */ |
||
461 | public function getContiguous() |
||
465 | |||
466 | /** |
||
467 | * Can the current IReader read the file? |
||
468 | * |
||
469 | * @param string $pFilename |
||
470 | * |
||
471 | * @throws Exception |
||
472 | * |
||
473 | * @return bool |
||
474 | */ |
||
475 | 3 | public function canRead($pFilename) |
|
488 | } |
||
489 |
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.