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 MSSQLDatabase 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 MSSQLDatabase, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
41 | class MSSQLDatabase extends SS_Database |
||
|
|||
42 | { |
||
43 | |||
44 | /** |
||
45 | * Words that will trigger an error if passed to a SQL Server fulltext search |
||
46 | */ |
||
47 | public static $noiseWords = array('about', '1', 'after', '2', 'all', 'also', '3', 'an', '4', 'and', '5', 'another', '6', 'any', '7', 'are', '8', 'as', '9', 'at', '0', 'be', '$', 'because', 'been', 'before', 'being', 'between', 'both', 'but', 'by', 'came', 'can', 'come', 'could', 'did', 'do', 'does', 'each', 'else', 'for', 'from', 'get', 'got', 'has', 'had', 'he', 'have', 'her', 'here', 'him', 'himself', 'his', 'how', 'if', 'in', 'into', 'is', 'it', 'its', 'just', 'like', 'make', 'many', 'me', 'might', 'more', 'most', 'much', 'must', 'my', 'never', 'no', 'now', 'of', 'on', 'only', 'or', 'other', 'our', 'out', 'over', 're', 'said', 'same', 'see', 'should', 'since', 'so', 'some', 'still', 'such', 'take', 'than', 'that', 'the', 'their', 'them', 'then', 'there', 'these', 'they', 'this', 'those', 'through', 'to', 'too', 'under', 'up', 'use', 'very', 'want', 'was', 'way', 'we', 'well', 'were', 'what', 'when', 'where', 'which', 'while', 'who', 'will', 'with', 'would', 'you', 'your', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'); |
||
48 | |||
49 | /** |
||
50 | * Transactions will work with FreeTDS, but not entirely with sqlsrv driver on Windows with MARS enabled. |
||
51 | * TODO: |
||
52 | * - after the test fails with open transaction, the transaction should be rolled back, |
||
53 | * otherwise other tests will break claiming that transaction is still open. |
||
54 | * - figure out SAVEPOINTS |
||
55 | * - READ ONLY transactions |
||
56 | */ |
||
57 | protected $supportsTransactions = true; |
||
58 | |||
59 | /** |
||
60 | * Cached flag to determine if full-text is enabled. This is set by |
||
61 | * {@link MSSQLDatabase::fullTextEnabled()} |
||
62 | * |
||
63 | * @var boolean |
||
64 | */ |
||
65 | protected $fullTextEnabled = null; |
||
66 | |||
67 | /** |
||
68 | * Set the default collation of the MSSQL nvarchar fields that we create. |
||
69 | * We don't apply this to the database as a whole, so that we can use unicode collations. |
||
70 | * |
||
71 | * @param string $collation |
||
72 | */ |
||
73 | public static function set_collation($collation) |
||
77 | |||
78 | /** |
||
79 | * The default collation of the MSSQL nvarchar fields that we create. |
||
80 | * We don't apply this to the database as a whole, so that we can use |
||
81 | * unicode collations. |
||
82 | * |
||
83 | * @return string |
||
84 | */ |
||
85 | public static function get_collation() |
||
89 | |||
90 | /** |
||
91 | * Connect to a MS SQL database. |
||
92 | * @param array $parameters An map of parameters, which should include: |
||
93 | * - server: The server, eg, localhost |
||
94 | * - username: The username to log on with |
||
95 | * - password: The password to log on with |
||
96 | * - database: The database to connect to |
||
97 | * - windowsauthentication: Set to true to use windows authentication |
||
98 | * instead of username/password |
||
99 | */ |
||
100 | public function connect($parameters) |
||
108 | |||
109 | /** |
||
110 | * Checks whether the current SQL Server version has full-text |
||
111 | * support installed and full-text is enabled for this database. |
||
112 | * |
||
113 | * @return boolean |
||
114 | */ |
||
115 | public function fullTextEnabled() |
||
122 | |||
123 | /** |
||
124 | * Checks whether the current SQL Server version has full-text |
||
125 | * support installed and full-text is enabled for this database. |
||
126 | * |
||
127 | * @return boolean |
||
128 | */ |
||
129 | protected function updateFullTextEnabled() |
||
145 | |||
146 | public function supportsCollations() |
||
150 | |||
151 | public function supportsTimezoneOverride() |
||
155 | |||
156 | public function getDatabaseServer() |
||
160 | |||
161 | public function selectDatabase($name, $create = false, $errorLevel = E_USER_ERROR) |
||
167 | |||
168 | public function clearTable($table) |
||
172 | |||
173 | /** |
||
174 | * SQL Server uses CURRENT_TIMESTAMP for the current date/time. |
||
175 | */ |
||
176 | public function now() |
||
180 | |||
181 | /** |
||
182 | * Returns the database-specific version of the random() function |
||
183 | */ |
||
184 | public function random() |
||
188 | |||
189 | /** |
||
190 | * The core search engine configuration. |
||
191 | * Picks up the fulltext-indexed tables from the database and executes search on all of them. |
||
192 | * Results are obtained as ID-ClassName pairs which is later used to reconstruct the DataObjectSet. |
||
193 | * |
||
194 | * @param array classesToSearch computes all descendants and includes them. Check is done via WHERE clause. |
||
195 | * @param string $keywords Keywords as a space separated string |
||
196 | * @return object DataObjectSet of result pages |
||
197 | */ |
||
198 | public function searchEngine($classesToSearch, $keywords, $start, $pageLength, $sortBy = "Relevance DESC", $extraFilter = "", $booleanSearch = false, $alternativeFileFilter = "", $invertedMatch = false) |
||
325 | |||
326 | /** |
||
327 | * Allow auto-increment primary key editing on the given table. |
||
328 | * Some databases need to enable this specially. |
||
329 | * |
||
330 | * @param $table The name of the table to have PK editing allowed on |
||
331 | * @param $allow True to start, false to finish |
||
332 | */ |
||
333 | public function allowPrimaryKeyEditing($table, $allow = true) |
||
337 | |||
338 | /** |
||
339 | * Returns a SQL fragment for querying a fulltext search index |
||
340 | * |
||
341 | * @param $tableName specific - table name |
||
342 | * @param $keywords string The search query |
||
343 | * @param $fields array The list of field names to search on, or null to include all |
||
344 | * |
||
345 | * @returns null if keyword set is empty or the string with JOIN clause to be added to SQL query |
||
346 | */ |
||
347 | public function fullTextSearchMSSQL($tableName, $keywords, $fields = null) |
||
380 | |||
381 | /** |
||
382 | * Remove stopwords that would kill a MSSQL full-text query |
||
383 | * |
||
384 | * @param array $keywords |
||
385 | * |
||
386 | * @return array $keywords with stopwords removed |
||
387 | */ |
||
388 | public function removeStopwords($keywords) |
||
399 | |||
400 | /** |
||
401 | * Does this database support transactions? |
||
402 | */ |
||
403 | public function supportsTransactions() |
||
407 | |||
408 | /** |
||
409 | * This is a quick lookup to discover if the database supports particular extensions |
||
410 | * Currently, MSSQL supports no extensions |
||
411 | * |
||
412 | * @param array $extensions List of extensions to check for support of. The key of this array |
||
413 | * will be an extension name, and the value the configuration for that extension. This |
||
414 | * could be one of partitions, tablespaces, or clustering |
||
415 | * @return boolean Flag indicating support for all of the above |
||
416 | */ |
||
417 | public function supportsExtensions($extensions = array('partitions', 'tablespaces', 'clustering')) |
||
429 | |||
430 | /** |
||
431 | * Start transaction. READ ONLY not supported. |
||
432 | */ |
||
433 | public function transactionStart($transactionMode = false, $sessionCharacteristics = false) |
||
441 | |||
442 | public function transactionSavepoint($savepoint) |
||
446 | |||
447 | public function transactionRollback($savepoint = false) |
||
457 | |||
458 | public function transactionEnd($chain = false) |
||
466 | |||
467 | public function comparisonClause($field, $value, $exact = false, $negate = false, $caseSensitive = null, $parameterised = false) |
||
504 | |||
505 | /** |
||
506 | * Function to return an SQL datetime expression for MSSQL |
||
507 | * used for querying a datetime in a certain format |
||
508 | * |
||
509 | * @param string $date to be formated, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' |
||
510 | * @param string $format to be used, supported specifiers: |
||
511 | * %Y = Year (four digits) |
||
512 | * %m = Month (01..12) |
||
513 | * %d = Day (01..31) |
||
514 | * %H = Hour (00..23) |
||
515 | * %i = Minutes (00..59) |
||
516 | * %s = Seconds (00..59) |
||
517 | * %U = unix timestamp, can only be used on it's own |
||
518 | * @return string SQL datetime expression to query for a formatted datetime |
||
519 | */ |
||
520 | public function formattedDatetimeClause($date, $format) |
||
570 | |||
571 | /** |
||
572 | * Function to return an SQL datetime expression for MSSQL. |
||
573 | * used for querying a datetime addition |
||
574 | * |
||
575 | * @param string $date, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' |
||
576 | * @param string $interval to be added, use the format [sign][integer] [qualifier], e.g. -1 Day, +15 minutes, +1 YEAR |
||
577 | * supported qualifiers: |
||
578 | * - years |
||
579 | * - months |
||
580 | * - days |
||
581 | * - hours |
||
582 | * - minutes |
||
583 | * - seconds |
||
584 | * This includes the singular forms as well |
||
585 | * @return string SQL datetime expression to query for a datetime (YYYY-MM-DD hh:mm:ss) which is the result of the addition |
||
586 | */ |
||
587 | public function datetimeIntervalClause($date, $interval) |
||
615 | |||
616 | /** |
||
617 | * Function to return an SQL datetime expression for MSSQL. |
||
618 | * used for querying a datetime substraction |
||
619 | * |
||
620 | * @param string $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' |
||
621 | * @param string $date2 to be substracted of $date1, can be either 'now', literal datetime like '1973-10-14 10:30:00' or field name, e.g. '"SiteTree"."Created"' |
||
622 | * @return string SQL datetime expression to query for the interval between $date1 and $date2 in seconds which is the result of the substraction |
||
623 | */ |
||
624 | public function datetimeDifferenceClause($date1, $date2) |
||
640 | } |
||
641 |
You can fix this by adding a namespace to your class:
When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.