Complex classes like Migrate 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 Migrate, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
34 | class Migrate |
||
35 | { |
||
36 | |||
37 | /** @var false|\Xmf\Module\Helper|\Xoops\Module\Helper\HelperAbstract */ |
||
38 | protected $helper; |
||
39 | |||
40 | /** @var string[] table names used by module */ |
||
41 | protected $moduleTables; |
||
42 | |||
43 | /** @var Tables */ |
||
44 | protected $tableHandler; |
||
45 | |||
46 | /** @var string yaml definition file */ |
||
47 | protected $tableDefinitionFile; |
||
48 | |||
49 | /** @var array target table definitions in Xmf\Database\Tables::dumpTables() format */ |
||
50 | protected $targetDefinitions; |
||
51 | |||
52 | /** |
||
53 | * Migrate constructor |
||
54 | * |
||
55 | * @param string $dirname module directory name that defines the tables to be migrated |
||
56 | * |
||
57 | * @throws \InvalidArgumentException |
||
58 | * @throws \RuntimeException |
||
59 | */ |
||
60 | 1 | public function __construct($dirname) |
|
75 | |||
76 | /** |
||
77 | * Save current table definitions to a file |
||
78 | * |
||
79 | * This is intended for developer use when setting up the migration by using the current database state |
||
80 | * |
||
81 | * @internal intended for module developers only |
||
82 | * |
||
83 | * @return int|false count of bytes written or false on error |
||
84 | */ |
||
85 | public function saveCurrentSchema() |
||
97 | |||
98 | /** |
||
99 | * get the current definitions |
||
100 | * |
||
101 | * @return array |
||
102 | */ |
||
103 | public function getCurrentSchema() |
||
111 | |||
112 | /** |
||
113 | * Return the target database condition |
||
114 | * |
||
115 | * @return array|bool table structure or false on error |
||
116 | * |
||
117 | * @throws \RuntimeException |
||
118 | */ |
||
119 | public function getTargetDefinitions() |
||
129 | |||
130 | /** |
||
131 | * Execute synchronization to transform current schema to target |
||
132 | * |
||
133 | * @param bool $force true to force updates even if this is a 'GET' request |
||
134 | * |
||
135 | * @return bool true if no errors, false if errors encountered |
||
136 | */ |
||
137 | public function synchronizeSchema($force = true) |
||
143 | |||
144 | /** |
||
145 | * Compare target and current schema, building work queue in $this->migrate to synchronized |
||
146 | * |
||
147 | * @return string[] array of DDL/SQL statements to transform current to target schema |
||
148 | */ |
||
149 | public function getSynchronizeDDL() |
||
150 | { |
||
151 | $this->getTargetDefinitions(); |
||
152 | $this->preSyncActions(); |
||
153 | foreach ($this->moduleTables as $tableName) { |
||
154 | if ($this->tableHandler->useTable($tableName)) { |
||
155 | $this->synchronizeTable($tableName); |
||
156 | } else { |
||
157 | $this->addMissingTable($tableName); |
||
158 | } |
||
159 | } |
||
160 | return $this->tableHandler->dumpQueue(); |
||
161 | } |
||
162 | |||
163 | /** |
||
164 | * Perform any upfront actions before synchronizing the schema. |
||
165 | * |
||
166 | * The schema comparison cannot recognize changes such as renamed columns or renamed tables. By overriding |
||
167 | * this method, an implementation can provide the logic to accomplish these types of changes, and leave |
||
168 | * the other details to be handled by synchronizeSchema(). |
||
169 | * |
||
170 | * An suitable implementation should be provided by a module by extending Migrate to define any required |
||
171 | * actions. |
||
172 | * |
||
173 | * Some typical uses include: |
||
174 | * - table and column renames |
||
175 | * - data conversions |
||
176 | * - move column data |
||
177 | * |
||
178 | * @return void |
||
179 | */ |
||
180 | protected function preSyncActions() |
||
183 | |||
184 | /** |
||
185 | * Add table create DDL to the work queue |
||
186 | * |
||
187 | * @param string $tableName table to add |
||
188 | * |
||
189 | * @return void |
||
190 | */ |
||
191 | protected function addMissingTable($tableName) |
||
206 | |||
207 | /** |
||
208 | * Build any DDL required to synchronize an existing table to match the target schema |
||
209 | * |
||
210 | * @param string $tableName table to synchronize |
||
211 | * |
||
212 | * @return void |
||
213 | */ |
||
214 | protected function synchronizeTable($tableName) |
||
264 | |||
265 | /** |
||
266 | * determine if a column on a table exists in the target definitions |
||
267 | * |
||
268 | * @param string $tableName table containing the column |
||
269 | * @param string $columnName column to check |
||
270 | * |
||
271 | * @return bool true if table and column combination is defined, otherwise false |
||
272 | */ |
||
273 | protected function targetHasColumn($tableName, $columnName) |
||
285 | |||
286 | /** |
||
287 | * determine if a table exists in the target definitions |
||
288 | * |
||
289 | * @param string $tableName table containing the column |
||
290 | * |
||
291 | * @return bool true if table is defined, otherwise false |
||
292 | */ |
||
293 | protected function targetHasTable($tableName) |
||
300 | |||
301 | /** |
||
302 | * Return message from last error encountered |
||
303 | * |
||
304 | * @return string last error message |
||
305 | */ |
||
306 | 1 | public function getLastError() |
|
310 | |||
311 | /** |
||
312 | * Return code from last error encountered |
||
313 | * |
||
314 | * @return int last error number |
||
315 | */ |
||
316 | 1 | public function getLastErrNo() |
|
320 | } |
||
321 |
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.