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 Root 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 Root, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | class Root extends \PhpSpreadsheet\Shared\OLE\PPS |
||
31 | { |
||
32 | /** |
||
33 | * Directory for temporary files |
||
34 | * @var string |
||
35 | */ |
||
36 | protected $tempDirectory = null; |
||
37 | |||
38 | /** |
||
39 | * @param int $time_1st A timestamp |
||
40 | * @param int $time_2nd A timestamp |
||
41 | * @param File[] $raChild |
||
42 | */ |
||
43 | public function __construct($time_1st, $time_2nd, $raChild) |
||
49 | |||
50 | /** |
||
51 | * Method for saving the whole OLE container (including files). |
||
52 | * In fact, if called with an empty argument (or '-'), it saves to a |
||
53 | * temporary file and then outputs it's contents to stdout. |
||
54 | * If a resource pointer to a stream created by fopen() is passed |
||
55 | * it will be used, but you have to close such stream by yourself. |
||
56 | * |
||
57 | * @param string|resource $filename The name of the file or stream where to save the OLE container. |
||
58 | * @throws \PhpSpreadsheet\Writer\Exception |
||
59 | * @return bool true on success |
||
60 | */ |
||
61 | public function save($filename) |
||
62 | { |
||
63 | // Initial Setting for saving |
||
64 | $this->_BIG_BLOCK_SIZE = pow( |
||
65 | 2, |
||
66 | (isset($this->_BIG_BLOCK_SIZE)) ? self::adjust2($this->_BIG_BLOCK_SIZE) : 9 |
||
67 | ); |
||
68 | $this->_SMALL_BLOCK_SIZE = pow( |
||
69 | 2, |
||
70 | (isset($this->_SMALL_BLOCK_SIZE)) ? self::adjust2($this->_SMALL_BLOCK_SIZE) : 6 |
||
71 | ); |
||
72 | |||
73 | if (is_resource($filename)) { |
||
74 | $this->_FILEH_ = $filename; |
||
75 | } elseif ($filename == '-' || $filename == '') { |
||
76 | if ($this->tempDirectory === null) { |
||
77 | $this->tempDirectory = \PhpSpreadsheet\Shared\File::sysGetTempDir(); |
||
78 | } |
||
79 | $this->_tmp_filename = tempnam($this->tempDirectory, 'OLE_PPS_Root'); |
||
80 | $this->_FILEH_ = fopen($this->_tmp_filename, 'w+b'); |
||
81 | if ($this->_FILEH_ == false) { |
||
82 | throw new \PhpSpreadsheet\Writer\Exception("Can't create temporary file."); |
||
83 | } |
||
84 | } else { |
||
85 | $this->_FILEH_ = fopen($filename, 'wb'); |
||
86 | } |
||
87 | if ($this->_FILEH_ == false) { |
||
88 | throw new \PhpSpreadsheet\Writer\Exception("Can't open $filename. It may be in use or protected."); |
||
89 | } |
||
90 | // Make an array of PPS's (for Save) |
||
91 | $aList = []; |
||
92 | \PhpSpreadsheet\Shared\OLE\PPS::_savePpsSetPnt($aList, [$this]); |
||
93 | // calculate values for header |
||
94 | list($iSBDcnt, $iBBcnt, $iPPScnt) = $this->_calcSize($aList); //, $rhInfo); |
||
95 | // Save Header |
||
96 | $this->_saveHeader($iSBDcnt, $iBBcnt, $iPPScnt); |
||
97 | |||
98 | // Make Small Data string (write SBD) |
||
99 | $this->_data = $this->_makeSmallData($aList); |
||
100 | |||
101 | // Write BB |
||
102 | $this->_saveBigData($iSBDcnt, $aList); |
||
103 | // Write PPS |
||
104 | $this->_savePps($aList); |
||
105 | // Write Big Block Depot and BDList and Adding Header informations |
||
106 | $this->_saveBbd($iSBDcnt, $iBBcnt, $iPPScnt); |
||
107 | |||
108 | if (!is_resource($filename)) { |
||
109 | fclose($this->_FILEH_); |
||
110 | } |
||
111 | |||
112 | return true; |
||
113 | } |
||
114 | |||
115 | /** |
||
116 | * Calculate some numbers |
||
117 | * |
||
118 | * @param array $raList Reference to an array of PPS's |
||
119 | * @return float[] The array of numbers |
||
120 | */ |
||
121 | public function _calcSize(&$raList) |
||
122 | { |
||
123 | // Calculate Basic Setting |
||
124 | list($iSBDcnt, $iBBcnt, $iPPScnt) = [0, 0, 0]; |
||
125 | $iSmallLen = 0; |
||
126 | $iSBcnt = 0; |
||
127 | $iCount = count($raList); |
||
128 | for ($i = 0; $i < $iCount; ++$i) { |
||
129 | if ($raList[$i]->Type == \PhpSpreadsheet\Shared\OLE::OLE_PPS_TYPE_FILE) { |
||
130 | $raList[$i]->Size = $raList[$i]->getDataLen(); |
||
131 | if ($raList[$i]->Size < \PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL) { |
||
132 | $iSBcnt += floor($raList[$i]->Size / $this->_SMALL_BLOCK_SIZE) |
||
133 | + (($raList[$i]->Size % $this->_SMALL_BLOCK_SIZE) ? 1 : 0); |
||
134 | } else { |
||
135 | $iBBcnt += (floor($raList[$i]->Size / $this->_BIG_BLOCK_SIZE) + |
||
136 | (($raList[$i]->Size % $this->_BIG_BLOCK_SIZE) ? 1 : 0)); |
||
137 | } |
||
138 | } |
||
139 | } |
||
140 | $iSmallLen = $iSBcnt * $this->_SMALL_BLOCK_SIZE; |
||
141 | $iSlCnt = floor($this->_BIG_BLOCK_SIZE / \PhpSpreadsheet\Shared\OLE::OLE_LONG_INT_SIZE); |
||
142 | $iSBDcnt = floor($iSBcnt / $iSlCnt) + (($iSBcnt % $iSlCnt) ? 1 : 0); |
||
143 | $iBBcnt += (floor($iSmallLen / $this->_BIG_BLOCK_SIZE) + |
||
144 | (($iSmallLen % $this->_BIG_BLOCK_SIZE) ? 1 : 0)); |
||
145 | $iCnt = count($raList); |
||
146 | $iBdCnt = $this->_BIG_BLOCK_SIZE / \PhpSpreadsheet\Shared\OLE::OLE_PPS_SIZE; |
||
147 | $iPPScnt = (floor($iCnt / $iBdCnt) + (($iCnt % $iBdCnt) ? 1 : 0)); |
||
148 | |||
149 | return [$iSBDcnt, $iBBcnt, $iPPScnt]; |
||
150 | } |
||
151 | |||
152 | /** |
||
153 | * Helper function for caculating a magic value for block sizes |
||
154 | * |
||
155 | * @param int $i2 The argument |
||
156 | * @see save() |
||
157 | * @return float |
||
158 | */ |
||
159 | private static function adjust2($i2) |
||
165 | |||
166 | /** |
||
167 | * Save OLE header |
||
168 | * |
||
169 | * @param int $iSBDcnt |
||
170 | * @param int $iBBcnt |
||
171 | * @param int $iPPScnt |
||
172 | */ |
||
173 | public function _saveHeader($iSBDcnt, $iBBcnt, $iPPScnt) |
||
245 | |||
246 | /** |
||
247 | * Saving big data (PPS's with data bigger than \PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL) |
||
248 | * |
||
249 | * @param int $iStBlk |
||
250 | * @param array &$raList Reference to array of PPS's |
||
251 | */ |
||
252 | public function _saveBigData($iStBlk, &$raList) |
||
276 | |||
277 | /** |
||
278 | * get small data (PPS's with data smaller than \PhpSpreadsheet\Shared\OLE::OLE_DATA_SIZE_SMALL) |
||
279 | * |
||
280 | * @param array &$raList Reference to array of PPS's |
||
281 | */ |
||
282 | public function _makeSmallData(&$raList) |
||
326 | |||
327 | /** |
||
328 | * Saves all the PPS's WKs |
||
329 | * |
||
330 | * @param array $raList Reference to an array with all PPS's |
||
331 | */ |
||
332 | public function _savePps(&$raList) |
||
346 | |||
347 | /** |
||
348 | * Saving Big Block Depot |
||
349 | * |
||
350 | * @param int $iSbdSize |
||
351 | * @param int $iBsize |
||
352 | * @param int $iPpsCnt |
||
353 | */ |
||
354 | public function _saveBbd($iSbdSize, $iBsize, $iPpsCnt) |
||
434 | } |
||
435 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: