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 MySQLiByDanielGP 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 MySQLiByDanielGP, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 36 | trait MySQLiByDanielGP |
||
| 37 | { |
||
| 38 | |||
| 39 | use DomComponentsByDanielGP, |
||
| 40 | MySQLiMultiple, |
||
| 41 | MySQLiByDanielGPqueries; |
||
| 42 | |||
| 43 | /** |
||
| 44 | * Intiates connection to MySQL |
||
| 45 | * |
||
| 46 | * @param array $mySQLconfig |
||
| 47 | * |
||
| 48 | * $mySQLconfig = [ |
||
| 49 | * 'host' => MYSQL_HOST, |
||
| 50 | * 'port' => MYSQL_PORT, |
||
| 51 | * 'username' => MYSQL_USERNAME, |
||
| 52 | * 'password' => MYSQL_PASSWORD, |
||
| 53 | * 'database' => MYSQL_DATABASE, |
||
| 54 | * ]; |
||
| 55 | */ |
||
| 56 | protected function connectToMySql($mySQLconfig) |
||
| 57 | { |
||
| 58 | if (is_null($this->mySQLconnection)) { |
||
| 59 | extract($mySQLconfig); |
||
| 60 | $this->mySQLconnection = new \mysqli($host, $username, $password, $database, $port); |
||
| 61 | if (is_null($this->mySQLconnection->connect_error)) { |
||
| 62 | return ''; |
||
| 63 | } |
||
| 64 | $erNo = $this->mySQLconnection->connect_errno; |
||
| 65 | $erMsg = $this->mySQLconnection->connect_error; |
||
| 66 | $this->mySQLconnection = null; |
||
| 67 | $msg = $this->lclMsgCmn('i18n_Feedback_ConnectionError'); |
||
| 68 | return sprintf($msg, $erNo, $erMsg, $host, $port, $username, $database); |
||
| 69 | } |
||
| 70 | } |
||
| 71 | |||
| 72 | /** |
||
| 73 | * returns a list of MySQL databases |
||
| 74 | * |
||
| 75 | * @return array |
||
| 76 | */ |
||
| 77 | protected function getMySQLactiveDatabases() |
||
| 78 | { |
||
| 79 | return $this->getMySQLlistDatabases(true); |
||
| 80 | } |
||
| 81 | |||
| 82 | /** |
||
| 83 | * returns a list of active MySQL engines |
||
| 84 | * |
||
| 85 | * @return array |
||
| 86 | */ |
||
| 87 | protected function getMySQLactiveEngines() |
||
| 88 | { |
||
| 89 | return $this->getMySQLlistEngines(true); |
||
| 90 | } |
||
| 91 | |||
| 92 | /** |
||
| 93 | * returns the list of all MySQL generic informations |
||
| 94 | * |
||
| 95 | * @return array |
||
| 96 | */ |
||
| 97 | protected function getMySQLgenericInformations() |
||
| 98 | { |
||
| 99 | if (is_null($this->mySQLconnection)) { |
||
| 100 | $line = []; |
||
| 101 | } else { |
||
| 102 | $line = [ |
||
| 103 | 'Info' => $this->mySQLconnection->server_info, |
||
| 104 | 'Version' => $this->mySQLconnection->server_version |
||
| 105 | ]; |
||
| 106 | } |
||
| 107 | return $line; |
||
| 108 | } |
||
| 109 | |||
| 110 | /** |
||
| 111 | * returns the list of all MySQL global variables |
||
| 112 | * |
||
| 113 | * @return array |
||
| 114 | */ |
||
| 115 | protected function getMySQLglobalVariables() |
||
| 116 | { |
||
| 117 | return $this->getMySQLlistMultiple('VariablesGlobal', 'array_key_value'); |
||
| 118 | } |
||
| 119 | |||
| 120 | /** |
||
| 121 | * returns a list of MySQL indexes (w. choice of to choose any combination of db/table/column) |
||
| 122 | * |
||
| 123 | * @return array |
||
| 124 | */ |
||
| 125 | protected function getMySQLlistColumns($filterArray = null) |
||
| 126 | { |
||
| 127 | return $this->getMySQLlistMultiple('Columns', 'full_array_key_numbered', $filterArray); |
||
| 128 | } |
||
| 129 | |||
| 130 | /** |
||
| 131 | * returns a list of MySQL databases (w. choice of exclude/include the system ones) |
||
| 132 | * |
||
| 133 | * @return array |
||
| 134 | */ |
||
| 135 | protected function getMySQLlistDatabases($excludeSystemDbs = true) |
||
| 136 | { |
||
| 137 | return $this->getMySQLlistMultiple('Databases', 'array_first_key_rest_values', $excludeSystemDbs); |
||
| 138 | } |
||
| 139 | |||
| 140 | /** |
||
| 141 | * returns a list of MySQL engines (w. choice of return only the active ones) |
||
| 142 | * |
||
| 143 | * @return array |
||
| 144 | */ |
||
| 145 | protected function getMySQLlistEngines($onlyActiveOnes = true) |
||
| 146 | { |
||
| 147 | return $this->getMySQLlistMultiple('Engines', 'array_first_key_rest_values', $onlyActiveOnes); |
||
| 148 | } |
||
| 149 | |||
| 150 | /** |
||
| 151 | * returns a list of MySQL indexes (w. choice of to choose any combination of db/table/column) |
||
| 152 | * |
||
| 153 | * @return array |
||
| 154 | */ |
||
| 155 | protected function getMySQLlistIndexes($filterArray = null) |
||
| 156 | { |
||
| 157 | return $this->getMySQLlistMultiple('Indexes', 'full_array_key_numbered', $filterArray); |
||
| 158 | } |
||
| 159 | |||
| 160 | /** |
||
| 161 | * Return various informations (from predefined list) from the MySQL server |
||
| 162 | * |
||
| 163 | * @return string |
||
| 164 | */ |
||
| 165 | private function getMySQLlistMultiple($returnChoice, $returnType, $additionalFeatures = null) |
||
| 166 | { |
||
| 167 | if (is_null($this->mySQLconnection)) { |
||
| 168 | switch ($returnType) { |
||
| 169 | case 'value': |
||
| 170 | $line = null; |
||
| 171 | break; |
||
| 172 | default: |
||
| 173 | $line = []; |
||
| 174 | break; |
||
| 175 | } |
||
| 176 | } else { |
||
| 177 | $query = ''; |
||
| 178 | switch ($returnChoice) { |
||
| 179 | case 'Columns': |
||
| 180 | $query = $this->sQueryMySqlColumns($additionalFeatures); |
||
| 181 | break; |
||
| 182 | case 'Databases': |
||
| 183 | $query = $this->sQueryMySqlActiveDatabases($additionalFeatures); |
||
| 184 | break; |
||
| 185 | case 'Engines': |
||
| 186 | $query = $this->sQueryMySqlActiveEngines($additionalFeatures); |
||
| 187 | break; |
||
| 188 | case 'Indexes': |
||
| 189 | $query = $this->sQueryMySqlIndexes($additionalFeatures); |
||
| 190 | break; |
||
| 191 | case 'ServerTime': |
||
| 192 | $query = $this->sQueryMySqlServerTime(); |
||
| 193 | break; |
||
| 194 | case 'Statistics': |
||
| 195 | $query = $this->sQueryMySqlStatistics($additionalFeatures); |
||
| 196 | break; |
||
| 197 | case 'Tables': |
||
| 198 | $query = $this->sQueryMySqlTables($additionalFeatures); |
||
| 199 | break; |
||
| 200 | case 'VariablesGlobal': |
||
| 201 | $query = $this->sQueryMySqlGlobalVariables(); |
||
| 202 | break; |
||
| 203 | } |
||
| 204 | $line = $this->setMySQLquery2Server($query, $returnType)[ |
||
| 205 | 'result' |
||
| 206 | ]; |
||
| 207 | } |
||
| 208 | return $line; |
||
| 209 | } |
||
| 210 | |||
| 211 | /** |
||
| 212 | * Return the list of Tables from the MySQL server |
||
| 213 | * |
||
| 214 | * @return string |
||
| 215 | */ |
||
| 216 | protected function getMySQLStatistics($filterArray = null) |
||
| 217 | { |
||
| 218 | return $this->getMySQLlistMultiple('Statistics', 'full_array_key_numbered', $filterArray); |
||
| 219 | } |
||
| 220 | |||
| 221 | /** |
||
| 222 | * Return the list of Tables from the MySQL server |
||
| 223 | * |
||
| 224 | * @return string |
||
| 225 | */ |
||
| 226 | protected function getMySQLlistTables($filterArray = null) |
||
| 227 | { |
||
| 228 | return $this->getMySQLlistMultiple('Tables', 'full_array_key_numbered', $filterArray); |
||
| 229 | } |
||
| 230 | |||
| 231 | /** |
||
| 232 | * Returns the Query language type by scanning the 1st keyword from a given query |
||
| 233 | * |
||
| 234 | * @param input $sQuery |
||
| 235 | */ |
||
| 236 | protected function getMySQLqueryType($sQuery) |
||
| 237 | { |
||
| 238 | $queryPieces = explode(' ', $sQuery); |
||
| 239 | $statementTypes = $this->getMySQLqueryStatementType(); |
||
|
|
|||
| 240 | if (in_array($queryPieces[0], array_keys($statementTypes))) { |
||
| 241 | $type = $statementTypes[$queryPieces[0]]['Type']; |
||
| 242 | $sReturn = array_merge([ |
||
| 243 | 'detected1stKeywordWithinQuery' => $queryPieces[0], |
||
| 244 | $type => $this->getMySQLqueryLanguageType()[$type], |
||
| 245 | ], $statementTypes[$queryPieces[0]]); |
||
| 246 | } else { |
||
| 247 | $sReturn = [ |
||
| 248 | 'detected1stKeywordWithinQuery' => $queryPieces[0], |
||
| 249 | 'unknown' => [ |
||
| 250 | 'standsFor' => 'unknown', |
||
| 251 | 'description' => 'unknown', |
||
| 252 | ], |
||
| 253 | 'Type' => 'unknown', |
||
| 254 | 'Description' => 'unknown', |
||
| 255 | ]; |
||
| 256 | } |
||
| 257 | return $sReturn; |
||
| 258 | } |
||
| 259 | |||
| 260 | /** |
||
| 261 | * Provides a detection if given Query does contain a Parameter |
||
| 262 | * that may require statement processing later on |
||
| 263 | * |
||
| 264 | * @param string $sQuery |
||
| 265 | * @param string $paramIdentifier |
||
| 266 | * @return boolean |
||
| 267 | */ |
||
| 268 | protected function getMySQLqueryWithParameterIdentifier($sQuery, $paramIdentifier) |
||
| 269 | { |
||
| 270 | $sReturn = true; |
||
| 271 | if (strpos($sQuery, $paramIdentifier) === false) { |
||
| 272 | $sReturn = false; |
||
| 273 | } |
||
| 274 | return $sReturn; |
||
| 275 | } |
||
| 276 | |||
| 277 | /** |
||
| 278 | * Return the time from the MySQL server |
||
| 279 | * |
||
| 280 | * @return string |
||
| 281 | */ |
||
| 282 | protected function getMySQLserverTime() |
||
| 283 | { |
||
| 284 | return $this->getMySQLlistMultiple('ServerTime', 'value'); |
||
| 285 | } |
||
| 286 | |||
| 287 | /** |
||
| 288 | * Reads data from table into REQUEST super global |
||
| 289 | * |
||
| 290 | * @param string $tableName |
||
| 291 | * @param array $filtersArray |
||
| 292 | */ |
||
| 293 | protected function getRowDataFromTable($tableName, $filtersArray) |
||
| 294 | { |
||
| 295 | $query = $this->sQueryRowsFromTable([$tableName, $this->setArrayToFilterValues($filtersArray)]); |
||
| 296 | $rawData = $this->setMySQLquery2Server($query, 'array_pairs_key_value')['result']; |
||
| 297 | if (!is_null($rawData)) { |
||
| 298 | $this->initializeSprGlbAndSession(); |
||
| 299 | foreach ($rawData as $key => $value) { |
||
| 300 | $vToSet = str_replace(['\\\\"', '\\"', "\\\\'", "\\'"], ['"', '"', "'", "'"], $value); |
||
| 301 | $this->tCmnRequest->request->get($key, $vToSet); |
||
| 302 | } |
||
| 303 | } |
||
| 304 | } |
||
| 305 | |||
| 306 | /** |
||
| 307 | * Just to keep a list of type of language as array |
||
| 308 | * |
||
| 309 | * @return array |
||
| 310 | */ |
||
| 311 | private static function listOfMySQLqueryLanguageType() |
||
| 350 | |||
| 351 | /** |
||
| 352 | * Just to keep a list of statement types as array |
||
| 353 | * |
||
| 354 | * @return array |
||
| 355 | */ |
||
| 356 | private static function listOfMySQLqueryStatementType() |
||
| 510 | |||
| 511 | /** |
||
| 512 | * Transforms an array into usable filters |
||
| 513 | * |
||
| 514 | * @param array $entryArray |
||
| 515 | * @param string $referenceTable |
||
| 516 | * @return array |
||
| 517 | */ |
||
| 518 | private function setArrayToFilterValues($entryArray, $referenceTable = '') |
||
| 519 | { |
||
| 520 | $filters = ''; |
||
| 521 | if ($referenceTable != '') { |
||
| 522 | $referenceTable = '`' . $referenceTable . '`.'; |
||
| 523 | } |
||
| 524 | foreach ($entryArray as $key => $value) { |
||
| 525 | if (is_array($value)) { |
||
| 526 | $filters2 = ''; |
||
| 527 | foreach ($value as $value2) { |
||
| 528 | if ($value2 != '') { |
||
| 529 | if ($filters2 != '') { |
||
| 530 | $filters2 .= ','; |
||
| 531 | } |
||
| 532 | $filters2 .= '"' . $value2 . '"'; |
||
| 533 | } |
||
| 534 | } |
||
| 535 | if ($filters2 != '') { |
||
| 536 | if ($filters != '') { |
||
| 537 | $filters .= ' AND '; |
||
| 538 | } |
||
| 539 | $filters .= ' ' . $referenceTable . '`' . $key |
||
| 540 | . '` IN ("' . str_replace(',', '","', str_replace(["'", '"'], '', $filters2)) |
||
| 541 | . '")'; |
||
| 542 | } |
||
| 543 | } else { |
||
| 544 | if (($filters != '') && (!in_array($value, ['', '%%']))) { |
||
| 545 | $filters .= ' AND '; |
||
| 546 | } |
||
| 547 | if (!in_array($value, ['', '%%'])) { |
||
| 548 | if ((substr($value, 0, 1) == '%') && (substr($value, -1) == '%')) { |
||
| 549 | $filters .= ' ' . $key . ' LIKE "' . $value . '"'; |
||
| 550 | } else { |
||
| 551 | $filters .= ' ' . $key . ' = "' . $value . '"'; |
||
| 552 | } |
||
| 553 | } |
||
| 554 | } |
||
| 555 | } |
||
| 556 | return $filters; |
||
| 557 | } |
||
| 558 | |||
| 559 | private function stFldLmts($colType, $loLmt, $upLmt, $szN, $szUS) |
||
| 567 | |||
| 568 | private function stFldLmtsExact($cTp) |
||
| 569 | { |
||
| 583 | |||
| 584 | /** |
||
| 585 | * Returns maximum length for a given MySQL field |
||
| 586 | * |
||
| 587 | * @param string $field_full_type |
||
| 588 | * @return array |
||
| 589 | */ |
||
| 590 | protected function setFieldNumbers($fieldDetails, $outputFormated = false) |
||
| 602 | |||
| 603 | private function setFieldSpecific($fieldDetails) |
||
| 621 | |||
| 622 | /** |
||
| 623 | * Transmit Query to MySQL server and get results back |
||
| 624 | * |
||
| 625 | * @param string $sQuery |
||
| 626 | * @param string $sReturnType |
||
| 627 | * @param array $ftrs |
||
| 628 | * @return boolean|array|string |
||
| 629 | */ |
||
| 630 | protected function setMySQLquery2Server($sQuery, $sReturnType = null, $ftrs = null) |
||
| 700 | |||
| 701 | /** |
||
| 702 | * Turns a raw query result into various structures |
||
| 703 | * based on different predefined $parameters['returnType'] value |
||
| 704 | * |
||
| 705 | * @param array $parameters |
||
| 706 | * @return array as ['customError' => '...', 'result' => '...'] |
||
| 707 | */ |
||
| 708 | protected function setMySQLquery2ServerByPattern($parameters) |
||
| 828 | } |
||
| 829 |
This check looks for methods that are used by a trait but not required by it.
To illustrate, let’s look at the following code example
The trait
Idableprovides a methodequalsIdthat in turn relies on the methodgetId(). If this method does not exist on a class mixing in this trait, the method will fail.Adding the
getId()as an abstract method to the trait will make sure it is available.