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 DatabaseInstaller 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 DatabaseInstaller, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
30 | abstract class DatabaseInstaller { |
||
31 | |||
32 | /** |
||
33 | * The Installer object. |
||
34 | * |
||
35 | * @todo Naming this parent is confusing, 'installer' would be clearer. |
||
36 | * |
||
37 | * @var WebInstaller |
||
38 | */ |
||
39 | public $parent; |
||
40 | |||
41 | /** |
||
42 | * The database connection. |
||
43 | * |
||
44 | * @var Database |
||
45 | */ |
||
46 | public $db = null; |
||
47 | |||
48 | /** |
||
49 | * Internal variables for installation. |
||
50 | * |
||
51 | * @var array |
||
52 | */ |
||
53 | protected $internalDefaults = []; |
||
54 | |||
55 | /** |
||
56 | * Array of MW configuration globals this class uses. |
||
57 | * |
||
58 | * @var array |
||
59 | */ |
||
60 | protected $globalNames = []; |
||
61 | |||
62 | /** |
||
63 | * Return the internal name, e.g. 'mysql', or 'sqlite'. |
||
64 | */ |
||
65 | abstract public function getName(); |
||
66 | |||
67 | /** |
||
68 | * @return bool Returns true if the client library is compiled in. |
||
69 | */ |
||
70 | abstract public function isCompiled(); |
||
71 | |||
72 | /** |
||
73 | * Checks for installation prerequisites other than those checked by isCompiled() |
||
74 | * @since 1.19 |
||
75 | * @return Status |
||
76 | */ |
||
77 | public function checkPrerequisites() { |
||
80 | |||
81 | /** |
||
82 | * Get HTML for a web form that configures this database. Configuration |
||
83 | * at this time should be the minimum needed to connect and test |
||
84 | * whether install or upgrade is required. |
||
85 | * |
||
86 | * If this is called, $this->parent can be assumed to be a WebInstaller. |
||
87 | */ |
||
88 | abstract public function getConnectForm(); |
||
89 | |||
90 | /** |
||
91 | * Set variables based on the request array, assuming it was submitted |
||
92 | * via the form returned by getConnectForm(). Validate the connection |
||
93 | * settings by attempting to connect with them. |
||
94 | * |
||
95 | * If this is called, $this->parent can be assumed to be a WebInstaller. |
||
96 | * |
||
97 | * @return Status |
||
98 | */ |
||
99 | abstract public function submitConnectForm(); |
||
100 | |||
101 | /** |
||
102 | * Get HTML for a web form that retrieves settings used for installation. |
||
103 | * $this->parent can be assumed to be a WebInstaller. |
||
104 | * If the DB type has no settings beyond those already configured with |
||
105 | * getConnectForm(), this should return false. |
||
106 | * @return bool |
||
107 | */ |
||
108 | public function getSettingsForm() { |
||
111 | |||
112 | /** |
||
113 | * Set variables based on the request array, assuming it was submitted via |
||
114 | * the form return by getSettingsForm(). |
||
115 | * |
||
116 | * @return Status |
||
117 | */ |
||
118 | public function submitSettingsForm() { |
||
121 | |||
122 | /** |
||
123 | * Open a connection to the database using the administrative user/password |
||
124 | * currently defined in the session, without any caching. Returns a status |
||
125 | * object. On success, the status object will contain a Database object in |
||
126 | * its value member. |
||
127 | * |
||
128 | * @return Status |
||
129 | */ |
||
130 | abstract public function openConnection(); |
||
131 | |||
132 | /** |
||
133 | * Create the database and return a Status object indicating success or |
||
134 | * failure. |
||
135 | * |
||
136 | * @return Status |
||
137 | */ |
||
138 | abstract public function setupDatabase(); |
||
139 | |||
140 | /** |
||
141 | * Connect to the database using the administrative user/password currently |
||
142 | * defined in the session. Returns a status object. On success, the status |
||
143 | * object will contain a Database object in its value member. |
||
144 | * |
||
145 | * This will return a cached connection if one is available. |
||
146 | * |
||
147 | * @return Status |
||
148 | */ |
||
149 | public function getConnection() { |
||
164 | |||
165 | /** |
||
166 | * Apply a SQL source file to the database as part of running an installation step. |
||
167 | * |
||
168 | * @param string $sourceFileMethod |
||
169 | * @param string $stepName |
||
170 | * @param bool $archiveTableMustNotExist |
||
171 | * @return Status |
||
172 | */ |
||
173 | private function stepApplySourceFile( |
||
211 | |||
212 | /** |
||
213 | * Create database tables from scratch. |
||
214 | * |
||
215 | * @return Status |
||
216 | */ |
||
217 | public function createTables() { |
||
220 | |||
221 | /** |
||
222 | * Insert update keys into table to prevent running unneded updates. |
||
223 | * |
||
224 | * @return Status |
||
225 | */ |
||
226 | public function insertUpdateKeys() { |
||
229 | |||
230 | /** |
||
231 | * Return a path to the DBMS-specific SQL file if it exists, |
||
232 | * otherwise default SQL file |
||
233 | * |
||
234 | * @param IDatabase $db |
||
235 | * @param string $filename |
||
236 | * @return string |
||
237 | */ |
||
238 | View Code Duplication | private function getSqlFilePath( $db, $filename ) { |
|
248 | |||
249 | /** |
||
250 | * Return a path to the DBMS-specific schema file, |
||
251 | * otherwise default to tables.sql |
||
252 | * |
||
253 | * @param IDatabase $db |
||
254 | * @return string |
||
255 | */ |
||
256 | public function getSchemaPath( $db ) { |
||
259 | |||
260 | /** |
||
261 | * Return a path to the DBMS-specific update key file, |
||
262 | * otherwise default to update-keys.sql |
||
263 | * |
||
264 | * @param IDatabase $db |
||
265 | * @return string |
||
266 | */ |
||
267 | public function getUpdateKeysPath( $db ) { |
||
270 | |||
271 | /** |
||
272 | * Create the tables for each extension the user enabled |
||
273 | * @return Status |
||
274 | */ |
||
275 | public function createExtensionTables() { |
||
286 | |||
287 | /** |
||
288 | * Get the DBMS-specific options for LocalSettings.php generation. |
||
289 | * |
||
290 | * @return string |
||
291 | */ |
||
292 | abstract public function getLocalSettings(); |
||
293 | |||
294 | /** |
||
295 | * Override this to provide DBMS-specific schema variables, to be |
||
296 | * substituted into tables.sql and other schema files. |
||
297 | * @return array |
||
298 | */ |
||
299 | public function getSchemaVars() { |
||
302 | |||
303 | /** |
||
304 | * Set appropriate schema variables in the current database connection. |
||
305 | * |
||
306 | * This should be called after any request data has been imported, but before |
||
307 | * any write operations to the database. |
||
308 | */ |
||
309 | public function setupSchemaVars() { |
||
320 | |||
321 | /** |
||
322 | * Set up LBFactory so that wfGetDB() etc. works. |
||
323 | * We set up a special LBFactory instance which returns the current |
||
324 | * installer connection. |
||
325 | */ |
||
326 | public function enableLB() { |
||
340 | |||
341 | /** |
||
342 | * Perform database upgrades |
||
343 | * |
||
344 | * @return bool |
||
345 | */ |
||
346 | public function doUpgrade() { |
||
369 | |||
370 | /** |
||
371 | * Allow DB installers a chance to make last-minute changes before installation |
||
372 | * occurs. This happens before setupDatabase() or createTables() is called, but |
||
373 | * long after the constructor. Helpful for things like modifying setup steps :) |
||
374 | */ |
||
375 | public function preInstall() { |
||
377 | |||
378 | /** |
||
379 | * Allow DB installers a chance to make checks before upgrade. |
||
380 | */ |
||
381 | public function preUpgrade() { |
||
383 | |||
384 | /** |
||
385 | * Get an array of MW configuration globals that will be configured by this class. |
||
386 | * @return array |
||
387 | */ |
||
388 | public function getGlobalNames() { |
||
391 | |||
392 | /** |
||
393 | * Construct and initialise parent. |
||
394 | * This is typically only called from Installer::getDBInstaller() |
||
395 | * @param WebInstaller $parent |
||
396 | */ |
||
397 | public function __construct( $parent ) { |
||
400 | |||
401 | /** |
||
402 | * Convenience function. |
||
403 | * Check if a named extension is present. |
||
404 | * |
||
405 | * @param string $name |
||
406 | * @return bool |
||
407 | */ |
||
408 | protected static function checkExtension( $name ) { |
||
411 | |||
412 | /** |
||
413 | * Get the internationalised name for this DBMS. |
||
414 | * @return string |
||
415 | */ |
||
416 | public function getReadableName() { |
||
421 | |||
422 | /** |
||
423 | * Get a name=>value map of MW configuration globals for the default values. |
||
424 | * @return array |
||
425 | */ |
||
426 | public function getGlobalDefaults() { |
||
435 | |||
436 | /** |
||
437 | * Get a name=>value map of internal variables used during installation. |
||
438 | * @return array |
||
439 | */ |
||
440 | public function getInternalDefaults() { |
||
443 | |||
444 | /** |
||
445 | * Get a variable, taking local defaults into account. |
||
446 | * @param string $var |
||
447 | * @param mixed|null $default |
||
448 | * @return mixed |
||
449 | */ |
||
450 | public function getVar( $var, $default = null ) { |
||
461 | |||
462 | /** |
||
463 | * Convenience alias for $this->parent->setVar() |
||
464 | * @param string $name |
||
465 | * @param mixed $value |
||
466 | */ |
||
467 | public function setVar( $name, $value ) { |
||
470 | |||
471 | /** |
||
472 | * Get a labelled text box to configure a local variable. |
||
473 | * |
||
474 | * @param string $var |
||
475 | * @param string $label |
||
476 | * @param array $attribs |
||
477 | * @param string $helpData |
||
478 | * @return string |
||
479 | */ |
||
480 | View Code Duplication | public function getTextBox( $var, $label, $attribs = [], $helpData = "" ) { |
|
496 | |||
497 | /** |
||
498 | * Get a labelled password box to configure a local variable. |
||
499 | * Implements password hiding. |
||
500 | * |
||
501 | * @param string $var |
||
502 | * @param string $label |
||
503 | * @param array $attribs |
||
504 | * @param string $helpData |
||
505 | * @return string |
||
506 | */ |
||
507 | View Code Duplication | public function getPasswordBox( $var, $label, $attribs = [], $helpData = "" ) { |
|
523 | |||
524 | /** |
||
525 | * Get a labelled checkbox to configure a local boolean variable. |
||
526 | * |
||
527 | * @param string $var |
||
528 | * @param string $label |
||
529 | * @param array $attribs Optional. |
||
530 | * @param string $helpData Optional. |
||
531 | * @return string |
||
532 | */ |
||
533 | View Code Duplication | public function getCheckBox( $var, $label, $attribs = [], $helpData = "" ) { |
|
546 | |||
547 | /** |
||
548 | * Get a set of labelled radio buttons. |
||
549 | * |
||
550 | * @param array $params Parameters are: |
||
551 | * var: The variable to be configured (required) |
||
552 | * label: The message name for the label (required) |
||
553 | * itemLabelPrefix: The message name prefix for the item labels (required) |
||
554 | * values: List of allowed values (required) |
||
555 | * itemAttribs Array of attribute arrays, outer key is the value name (optional) |
||
556 | * |
||
557 | * @return string |
||
558 | */ |
||
559 | public function getRadioSet( $params ) { |
||
565 | |||
566 | /** |
||
567 | * Convenience function to set variables based on form data. |
||
568 | * Assumes that variables containing "password" in the name are (potentially |
||
569 | * fake) passwords. |
||
570 | * @param array $varNames |
||
571 | * @return array |
||
572 | */ |
||
573 | public function setVarsFromRequest( $varNames ) { |
||
576 | |||
577 | /** |
||
578 | * Determine whether an existing installation of MediaWiki is present in |
||
579 | * the configured administrative connection. Returns true if there is |
||
580 | * such a wiki, false if the database doesn't exist. |
||
581 | * |
||
582 | * Traditionally, this is done by testing for the existence of either |
||
583 | * the revision table or the cur table. |
||
584 | * |
||
585 | * @return bool |
||
586 | */ |
||
587 | public function needsUpgrade() { |
||
600 | |||
601 | /** |
||
602 | * Get a standard install-user fieldset. |
||
603 | * |
||
604 | * @return string |
||
605 | */ |
||
606 | public function getInstallUserBox() { |
||
623 | |||
624 | /** |
||
625 | * Submit a standard install user fieldset. |
||
626 | * @return Status |
||
627 | */ |
||
628 | public function submitInstallUserBox() { |
||
633 | |||
634 | /** |
||
635 | * Get a standard web-user fieldset |
||
636 | * @param string|bool $noCreateMsg Message to display instead of the creation checkbox. |
||
637 | * Set this to false to show a creation checkbox (default). |
||
638 | * |
||
639 | * @return string |
||
640 | */ |
||
641 | public function getWebUserBox( $noCreateMsg = false ) { |
||
662 | |||
663 | /** |
||
664 | * Submit the form from getWebUserBox(). |
||
665 | * |
||
666 | * @return Status |
||
667 | */ |
||
668 | public function submitWebUserBox() { |
||
684 | |||
685 | /** |
||
686 | * Common function for databases that don't understand the MySQLish syntax of interwiki.sql. |
||
687 | * |
||
688 | * @return Status |
||
689 | */ |
||
690 | public function populateInterwikiTable() { |
||
726 | |||
727 | public function outputHandler( $string ) { |
||
730 | } |
||
731 |
If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:
If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.