| Total Complexity | 49 |
| Total Lines | 326 |
| Duplicated Lines | 11.66 % |
| Changes | 0 | ||
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 SqlController 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.
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 SqlController, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 8 | class SqlController extends BaseController |
||
| 9 | { |
||
| 10 | public $_name = 'SqlController'; |
||
| 11 | public $query = ''; |
||
| 12 | public $subject = ''; |
||
| 13 | public $start_time = null; |
||
| 14 | public $duration = null; |
||
| 15 | |||
| 16 | public function render() |
||
| 83 | } |
||
| 84 | |||
| 85 | public function doDefault() |
||
| 107 | } |
||
| 108 | } |
||
| 109 | |||
| 110 | private function execute_script() |
||
| 111 | { |
||
| 112 | $conf = $this->conf; |
||
| 113 | $misc = $this->misc; |
||
| 114 | $lang = $this->lang; |
||
| 115 | $data = $misc->getDatabaseAccessor(); |
||
| 116 | $_connection = $misc->getConnection(); |
||
| 117 | |||
| 118 | /** |
||
| 119 | * This is a callback function to display the result of each separate query |
||
| 120 | * @param ADORecordSet $rs The recordset returned by the script execetor |
||
| 121 | */ |
||
| 122 | $sqlCallback = function ($query, $rs, $lineno) use ($data, $misc, $lang, $_connection) { |
||
| 123 | |||
| 124 | // Check if $rs is false, if so then there was a fatal error |
||
| 125 | if ($rs === false) { |
||
| 126 | echo htmlspecialchars($_FILES['script']['name']), ':', $lineno, ': ', nl2br(htmlspecialchars($_connection->getLastError())), "<br/>\n"; |
||
| 127 | } else { |
||
| 128 | // Print query results |
||
| 129 | switch (pg_result_status($rs)) { |
||
| 130 | case PGSQL_TUPLES_OK: |
||
| 131 | // If rows returned, then display the results |
||
| 132 | $num_fields = pg_numfields($rs); |
||
| 133 | echo "<p><table>\n<tr>"; |
||
| 134 | for ($k = 0; $k < $num_fields; $k++) { |
||
| 135 | echo '<th class="data">', $misc->printVal(pg_fieldname($rs, $k)), '</th>'; |
||
| 136 | } |
||
| 137 | |||
| 138 | $i = 0; |
||
| 139 | $row = pg_fetch_row($rs); |
||
| 140 | while ($row !== false) { |
||
| 141 | $id = (($i % 2) == 0 ? '1' : '2'); |
||
| 142 | echo "<tr class=\"data{$id}\">\n"; |
||
| 143 | foreach ($row as $k => $v) { |
||
| 144 | echo '<td style="white-space:nowrap;">', $misc->printVal($v, pg_fieldtype($rs, $k), ['null' => true]), '</td>'; |
||
| 145 | } |
||
| 146 | echo "</tr>\n"; |
||
| 147 | $row = pg_fetch_row($rs); |
||
| 148 | $i++; |
||
| 149 | } |
||
| 150 | ; |
||
| 151 | echo "</table><br/>\n"; |
||
| 152 | echo $i, " {$lang['strrows']}</p>\n"; |
||
| 153 | break; |
||
| 154 | case PGSQL_COMMAND_OK: |
||
| 155 | // If we have the command completion tag |
||
| 156 | if (version_compare(phpversion(), '4.3', '>=')) { |
||
| 157 | echo htmlspecialchars(pg_result_status($rs, PGSQL_STATUS_STRING)), "<br/>\n"; |
||
| 158 | } |
||
| 159 | // Otherwise if any rows have been affected |
||
| 160 | elseif ($data->conn->Affected_Rows() > 0) { |
||
| 161 | echo $data->conn->Affected_Rows(), " {$lang['strrowsaff']}<br/>\n"; |
||
| 162 | } |
||
| 163 | // Otherwise output nothing... |
||
| 164 | break; |
||
| 165 | case PGSQL_EMPTY_QUERY: |
||
| 166 | break; |
||
| 167 | default: |
||
| 168 | break; |
||
| 169 | } |
||
| 170 | } |
||
| 171 | }; |
||
| 172 | |||
| 173 | return $data->executeScript('script', $sqlCallback); |
||
| 174 | } |
||
| 175 | |||
| 176 | private function execute_query() |
||
| 177 | { |
||
| 178 | $conf = $this->conf; |
||
| 179 | $misc = $this->misc; |
||
| 180 | $lang = $this->lang; |
||
| 181 | $data = $misc->getDatabaseAccessor(); |
||
| 182 | |||
| 183 | // Set fetch mode to NUM so that duplicate field names are properly returned |
||
| 184 | $data->conn->setFetchMode(ADODB_FETCH_NUM); |
||
| 185 | |||
| 186 | $rs = $data->conn->Execute($this->query); |
||
| 187 | |||
| 188 | echo '<form method="post" id="sqlform" action="' . $_SERVER['REQUEST_URI'] . '">'; |
||
| 189 | echo '<textarea width="90%" name="query" id="query" rows="5" cols="100" resizable="true">'; |
||
| 190 | |||
| 191 | echo htmlspecialchars($this->query); |
||
| 192 | echo '</textarea><br>'; |
||
| 193 | echo $misc->setForm(); |
||
| 194 | echo '<input type="submit"/></form>'; |
||
| 195 | |||
| 196 | // $rs will only be an object if there is no error |
||
| 197 | if (is_object($rs)) { |
||
| 198 | // Request was run, saving it in history |
||
| 199 | if (!isset($_REQUEST['nohistory'])) { |
||
| 200 | $misc->saveScriptHistory($this->query); |
||
| 201 | } |
||
| 202 | |||
| 203 | // Now, depending on what happened do various things |
||
| 204 | |||
| 205 | // First, if rows returned, then display the results |
||
| 206 | if ($rs->recordCount() > 0) { |
||
| 207 | echo "<table>\n<tr>"; |
||
| 208 | View Code Duplication | foreach ($rs->fields as $k => $v) { |
|
| 209 | $finfo = $rs->fetchField($k); |
||
| 210 | echo '<th class="data">', $misc->printVal($finfo->name), '</th>'; |
||
| 211 | } |
||
| 212 | echo "</tr>\n"; |
||
| 213 | $i = 0; |
||
| 214 | while (!$rs->EOF) { |
||
| 215 | $id = (($i % 2) == 0 ? '1' : '2'); |
||
| 216 | echo "<tr class=\"data{$id}\">\n"; |
||
| 217 | View Code Duplication | foreach ($rs->fields as $k => $v) { |
|
| 218 | $finfo = $rs->fetchField($k); |
||
| 219 | echo '<td style="white-space:nowrap;">', $misc->printVal($v, $finfo->type, ['null' => true]), '</td>'; |
||
| 220 | } |
||
| 221 | echo "</tr>\n"; |
||
| 222 | $rs->moveNext(); |
||
| 223 | $i++; |
||
| 224 | } |
||
| 225 | echo "</table>\n"; |
||
| 226 | echo '<p>', $rs->recordCount(), " {$lang['strrows']}</p>\n"; |
||
| 227 | } elseif ($data->conn->Affected_Rows() > 0) { |
||
| 228 | // Otherwise if any rows have been affected |
||
| 229 | echo '<p>', $data->conn->Affected_Rows(), " {$lang['strrowsaff']}</p>\n"; |
||
| 230 | } else { |
||
| 231 | // Otherwise nodata to print |
||
| 232 | echo '<p>', $lang['strnodata'], "</p>\n"; |
||
| 233 | } |
||
| 234 | } |
||
| 235 | } |
||
| 236 | |||
| 237 | private function doFooter($doBody = true, $template = 'footer.twig') |
||
| 334 | } |
||
| 335 | } |
||
| 336 |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: