Complex classes like ShapeFile 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 ShapeFile, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
29 | class ShapeFile { |
||
30 | public $FileName; |
||
31 | |||
32 | private $SHPFile = null; |
||
33 | private $SHXFile = null; |
||
34 | private $DBFFile = null; |
||
35 | |||
36 | public $DBFHeader; |
||
37 | |||
38 | public $lastError = ''; |
||
39 | |||
40 | public $boundingBox = array('xmin' => 0.0, 'ymin' => 0.0, 'xmax' => 0.0, 'ymax' => 0.0); |
||
41 | private $fileLength = 0; |
||
42 | public $shapeType = 0; |
||
43 | |||
44 | public $records; |
||
45 | |||
46 | private static $shape_names = array( |
||
47 | 0 => 'Null Shape', |
||
48 | 1 => 'Point', |
||
49 | 3 => 'PolyLine', |
||
50 | 5 => 'Polygon', |
||
51 | 8 => 'MultiPoint', |
||
52 | 11 => 'PointZ', |
||
53 | 13 => 'PolyLineZ', |
||
54 | 15 => 'PolygonZ', |
||
55 | 18 => 'MultiPointZ', |
||
56 | 21 => 'PointM', |
||
57 | 23 => 'PolyLineM', |
||
58 | 25 => 'PolygonM', |
||
59 | 28 => 'MultiPointM', |
||
60 | 31 => 'MultiPatch', |
||
61 | ); |
||
62 | |||
63 | /** |
||
64 | * Checks whether dbase manipuations are supported. |
||
65 | * |
||
66 | * @return bool |
||
67 | */ |
||
68 | public static function supports_dbase() |
||
72 | |||
73 | /** |
||
74 | * @param integer $shapeType |
||
75 | */ |
||
76 | public function __construct($shapeType, $boundingBox = array('xmin' => 0.0, 'ymin' => 0.0, 'xmax' => 0.0, 'ymax' => 0.0), $FileName = null) { |
||
82 | |||
83 | /** |
||
84 | * @param string $FileName |
||
85 | */ |
||
86 | public function loadFromFile($FileName) { |
||
113 | |||
114 | /** |
||
115 | * @param string|null $FileName Name of file to open |
||
116 | */ |
||
117 | public function saveToFile($FileName = null) { |
||
132 | |||
133 | /** |
||
134 | * Generates filename with given extension |
||
135 | * |
||
136 | * @param string $extension Extension to use (including dot) |
||
137 | * |
||
138 | * @return string |
||
139 | */ |
||
140 | private function _getFilename($extension) |
||
144 | |||
145 | /** |
||
146 | * Updates bounding box based on SHPData |
||
147 | * |
||
148 | * @param string $type Type of box |
||
149 | * @param array $data ShapeRecord SHPData |
||
150 | * |
||
151 | * @return void |
||
152 | */ |
||
153 | private function updateBBox($type, $data) |
||
154 | { |
||
155 | $min = $type.'min'; |
||
156 | $max = $type.'max'; |
||
157 | |||
158 | if (!isset($this->boundingBox[$min]) || $this->boundingBox[$min] == 0.0 || ($this->boundingBox[$min] > $data[$min])) { |
||
1 ignored issue
–
show
|
|||
159 | $this->boundingBox[$min] = $data[$min]; |
||
160 | } |
||
161 | if (!isset($this->boundingBox[$max]) || $this->boundingBox[$max] == 0.0 || ($this->boundingBox[$max] < $data[$max])) { |
||
162 | $this->boundingBox[$max] = $data[$max]; |
||
163 | } |
||
164 | } |
||
165 | |||
166 | /** |
||
167 | * @param ShapeRecord $record |
||
168 | */ |
||
169 | public function addRecord($record) { |
||
170 | if ((isset($this->DBFHeader)) && (is_array($this->DBFHeader))) { |
||
171 | $record->updateDBFInfo($this->DBFHeader); |
||
172 | } |
||
173 | |||
174 | $this->fileLength += ($record->getContentLength() + 4); |
||
175 | $this->records[] = $record; |
||
176 | $this->records[count($this->records) - 1]->recordNumber = count($this->records); |
||
177 | |||
178 | $this->updateBBox('x', $record->SHPData); |
||
179 | $this->updateBBox('y', $record->SHPData); |
||
180 | |||
181 | if (in_array($this->shapeType, array(11, 13, 15, 18, 21, 23, 25, 28))) { |
||
182 | $this->updateBBox('m', $record->SHPData); |
||
183 | } |
||
184 | |||
185 | if (in_array($this->shapeType, array(11, 13, 15, 18))) { |
||
186 | $this->updateBBox('z', $record->SHPData); |
||
187 | } |
||
188 | |||
189 | return (count($this->records) - 1); |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * @param integer $index |
||
194 | */ |
||
195 | public function deleteRecord($index) { |
||
206 | |||
207 | public function getDBFHeader() { |
||
210 | |||
211 | public function setDBFHeader($header) { |
||
219 | |||
220 | public function getIndexFromDBFData($field, $value) { |
||
231 | |||
232 | private function _loadDBFHeader() { |
||
266 | |||
267 | private function _deleteRecordFromDBF($index) { |
||
272 | |||
273 | private function _loadHeaders() { |
||
304 | |||
305 | private function _saveHeaders() { |
||
332 | |||
333 | private function _loadRecords() { |
||
350 | |||
351 | private function _saveRecords() { |
||
352 | if (!ShapeFile::supports_dbase()) { |
||
353 | return; |
||
354 | } |
||
355 | $dbf_name = $this->_getFilename('.dbf'); |
||
356 | if (file_exists($dbf_name)) { |
||
357 | unlink($dbf_name); |
||
358 | } |
||
359 | if (!($this->DBFFile = @dbase_create($dbf_name, $this->DBFHeader))) { |
||
360 | $this->setError(sprintf('It wasn\'t possible to create the DBase file "%s"', $dbf_name)); |
||
361 | return false; |
||
362 | } |
||
363 | |||
364 | $offset = 50; |
||
365 | if (is_array($this->records) && (count($this->records) > 0)) { |
||
366 | foreach ($this->records as $index => $record) { |
||
367 | //Save the record to the .shp file |
||
368 | $record->saveToFile($this->SHPFile, $this->DBFFile, $index + 1); |
||
369 | |||
370 | //Save the record to the .shx file |
||
371 | fwrite($this->SHXFile, pack('N', $offset)); |
||
372 | fwrite($this->SHXFile, pack('N', $record->getContentLength())); |
||
373 | $offset += (4 + $record->getContentLength()); |
||
374 | } |
||
375 | } |
||
376 | dbase_pack($this->DBFFile); |
||
377 | } |
||
378 | |||
379 | private function _openFile($toWrite, $extension, $name) { |
||
389 | |||
390 | private function _openSHPFile($toWrite = false) { |
||
397 | |||
398 | private function _closeSHPFile() { |
||
404 | |||
405 | private function _openSHXFile($toWrite = false) { |
||
412 | |||
413 | private function _closeSHXFile() { |
||
419 | |||
420 | /** |
||
421 | * Loads DBF file if supported |
||
422 | * |
||
423 | * @return bool |
||
424 | */ |
||
425 | private function _openDBFFile($toWrite = false) { |
||
426 | if (!ShapeFile::supports_dbase()) { |
||
427 | return true; |
||
428 | } |
||
429 | $dbf_name = $this->_getFilename('.dbf'); |
||
430 | $checkFunction = $toWrite ? 'is_writable' : 'is_readable'; |
||
431 | if (($toWrite) && (!file_exists($dbf_name))) { |
||
432 | if (!@dbase_create($dbf_name, $this->DBFHeader)) { |
||
433 | $this->setError(sprintf('It wasn\'t possible to create the DBase file "%s"', $dbf_name)); |
||
434 | return false; |
||
435 | } |
||
436 | } |
||
437 | if ($checkFunction($dbf_name)) { |
||
438 | $this->DBFFile = @dbase_open($dbf_name, ($toWrite ? 2 : 0)); |
||
439 | if (!$this->DBFFile) { |
||
440 | $this->setError(sprintf('It wasn\'t possible to open the DBase file "%s"', $dbf_name)); |
||
441 | return false; |
||
442 | } |
||
443 | } else { |
||
444 | $this->setError(sprintf('It wasn\'t possible to find the DBase file "%s"', $dbf_name)); |
||
445 | return false; |
||
446 | } |
||
447 | return true; |
||
448 | } |
||
449 | |||
450 | private function _closeDBFFile() { |
||
456 | |||
457 | /** |
||
458 | * Sets error message |
||
459 | * |
||
460 | * @param string $error |
||
461 | * |
||
462 | * @return void |
||
463 | */ |
||
464 | public function setError($error) { |
||
467 | |||
468 | /** |
||
469 | * Reads given number of bytes from SHP file |
||
470 | * |
||
471 | * @param integer $bytes |
||
472 | * |
||
473 | * @return string |
||
474 | */ |
||
475 | public function readSHP($bytes) |
||
479 | |||
480 | /** |
||
481 | * Checks whether file is at EOF |
||
482 | * |
||
483 | * @return bool |
||
484 | */ |
||
485 | public function eofSHP() |
||
489 | |||
490 | /** |
||
491 | * Returns shape name |
||
492 | * |
||
493 | * @return string |
||
494 | */ |
||
495 | public function getShapeName() |
||
499 | |||
500 | /** |
||
501 | * Returns shape name |
||
502 | * |
||
503 | * @param integer $type |
||
504 | * |
||
505 | * @return string |
||
506 | */ |
||
507 | public static function nameShape($type) |
||
514 | |||
515 | /** |
||
516 | * Check whether file contains measure data. |
||
517 | * |
||
518 | * For some reason this is distinguished by zero bouding box in the |
||
519 | * specification. |
||
520 | * |
||
521 | * @return bool |
||
522 | */ |
||
523 | public function hasMeasure() |
||
527 | } |
||
528 | |||
529 |
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.