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 DbQuery 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 DbQuery, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
21 | class DbQuery { |
||
22 | |||
23 | const SELECT = 'SELECT'; |
||
24 | const REPLACE = 'REPLACE'; |
||
25 | const INSERT = 'INSERT'; |
||
26 | const INSERT_IGNORE = 'INSERT IGNORE'; |
||
27 | const UPDATE = 'UPDATE'; |
||
28 | const DELETE = 'DELETE'; |
||
29 | |||
30 | /** |
||
31 | * @var db_mysql |
||
32 | */ |
||
33 | protected $db; |
||
34 | |||
35 | /** |
||
36 | * Which command would be performed |
||
37 | * |
||
38 | * @var string $command |
||
39 | */ |
||
40 | protected $command; |
||
41 | |||
42 | protected $table = ''; |
||
43 | |||
44 | /** |
||
45 | * Contains field names integer keyed |
||
46 | * |
||
47 | * For SELECT {fields} FROM |
||
48 | * For INSERT/REPLACE {fields} UPDATE ... |
||
49 | * |
||
50 | * @var array $fields |
||
51 | */ |
||
52 | protected $fields = array(); |
||
53 | protected $where = array(); |
||
54 | protected $whereDanger = array(); |
||
55 | |||
56 | /** |
||
57 | * Contain array of values - fielded or not |
||
58 | * |
||
59 | * For INSERT/REPLACE ... SET, UPDATE ... SET contains fieldName => value |
||
60 | * For INSERT/REPLACE ... VALUES contains values[][] |
||
61 | * |
||
62 | * @var array |
||
63 | */ |
||
64 | protected $values = array(); |
||
65 | /** |
||
66 | * Contain array of DANGER values for batch INSERT/REPLACE |
||
67 | * |
||
68 | * @var string[] |
||
69 | */ |
||
70 | protected $valuesDanger = array(); |
||
71 | protected $adjust = array(); |
||
72 | protected $adjustDanger = array(); |
||
73 | |||
74 | |||
75 | /** |
||
76 | * Variable for incremental query build |
||
77 | * |
||
78 | * @var string[] $build |
||
79 | */ |
||
80 | protected $build = array(); |
||
81 | |||
82 | protected $isOneRow = false; |
||
83 | |||
84 | /** |
||
85 | * DbQuery constructor. |
||
86 | * |
||
87 | * @param null|\db_mysql $db |
||
88 | */ |
||
89 | // TODO - $db should be supplied externally |
||
90 | 1 | public function __construct($db = null) { |
|
93 | |||
94 | /** |
||
95 | * @param null|db_mysql $db |
||
96 | * |
||
97 | * @return static |
||
98 | */ |
||
99 | 1 | public static function build($db = null) { |
|
102 | |||
103 | |||
104 | 1 | View Code Duplication | public function delete() { |
114 | |||
115 | 1 | View Code Duplication | public function update() { |
126 | |||
127 | 1 | public function insertSet($replace) { |
|
147 | |||
148 | 1 | public function insertBatch($replace) { |
|
171 | |||
172 | |||
173 | |||
174 | |||
175 | |||
176 | |||
177 | |||
178 | |||
179 | |||
180 | /** |
||
181 | * @param $table |
||
182 | * |
||
183 | * @return $this |
||
184 | */ |
||
185 | 1 | public function setTable($table) { |
|
190 | |||
191 | /** |
||
192 | * @param bool $oneRow - DB_RECORDS_ALL || DB_RECORD_ONE |
||
193 | * |
||
194 | * @return $this |
||
195 | */ |
||
196 | 1 | public function setOneRow($oneRow = DB_RECORDS_ALL) { |
|
201 | |||
202 | /** |
||
203 | * @param array $values |
||
204 | * |
||
205 | * @return $this |
||
206 | */ |
||
207 | 1 | public function setValues($values = array()) { |
|
212 | |||
213 | /** |
||
214 | * @param array $values |
||
215 | * |
||
216 | * @return $this |
||
217 | */ |
||
218 | 1 | public function setValuesDanger($values = array()) { |
|
223 | |||
224 | /** |
||
225 | * @param array $values |
||
226 | * |
||
227 | * @return $this |
||
228 | */ |
||
229 | 1 | public function setAdjust($values = array()) { |
|
234 | |||
235 | /** |
||
236 | * @param array $values |
||
237 | * |
||
238 | * @return $this |
||
239 | */ |
||
240 | 1 | public function setAdjustDanger($values = array()) { |
|
245 | |||
246 | /** |
||
247 | * @param array $fields |
||
248 | * |
||
249 | * @return $this |
||
250 | */ |
||
251 | 1 | public function setFields($fields = array()) { |
|
256 | |||
257 | /** |
||
258 | * Merges WHERE array as array_merge() |
||
259 | * |
||
260 | * @param array $whereArray |
||
261 | * |
||
262 | * @return $this |
||
263 | */ |
||
264 | 1 | public function setWhereArray($whereArray = array()) { |
|
269 | |||
270 | /** |
||
271 | * Sets DANGER array - where values should be escaped BEFORE entering DBAL |
||
272 | * |
||
273 | * Deprecated - all values should pass through DBAL |
||
274 | * |
||
275 | * @param array $whereArrayDanger |
||
276 | * |
||
277 | * @return $this |
||
278 | */ |
||
279 | 1 | public function setWhereArrayDanger($whereArrayDanger = array()) { |
|
284 | |||
285 | |||
286 | /** |
||
287 | * Wrapper for db_escape() |
||
288 | * |
||
289 | * @param $string |
||
290 | * |
||
291 | * @return string |
||
292 | */ |
||
293 | 1 | protected function escape($string) { |
|
296 | |||
297 | 1 | protected function escapeEmulator($value) { |
|
305 | |||
306 | /** |
||
307 | * Escaping string value and quoting it |
||
308 | * |
||
309 | * @param mixed $value |
||
310 | * |
||
311 | * @return string |
||
312 | */ |
||
313 | 1 | protected function stringValue($value) { |
|
316 | |||
317 | /** |
||
318 | * Quote mysql DB identifier |
||
319 | * |
||
320 | * @param mixed $fieldName |
||
321 | * |
||
322 | * @return string |
||
323 | */ |
||
324 | 1 | protected function quote($fieldName) { |
|
327 | |||
328 | /** |
||
329 | * Quote table name with `{{ }}` |
||
330 | * |
||
331 | * @param mixed $tableName |
||
332 | * |
||
333 | * @return string |
||
334 | */ |
||
335 | 1 | protected function quoteTable($tableName) { |
|
338 | |||
339 | 13 | View Code Duplication | protected function castAsDbValue($value) { |
368 | |||
369 | |||
370 | // TODO - redo as callable usage with array_map/array_walk |
||
371 | /** |
||
372 | * Make string from DANGER item array |
||
373 | * |
||
374 | * This function is DANGER! It takes numeric indexes which translate to direct SQL string which can lead to SQL injection! |
||
375 | * |
||
376 | * @param array $where - array WHERE clauses which will not pass through SAFE filter |
||
377 | * |
||
378 | * @return array |
||
379 | */ |
||
380 | 3 | protected function packIntKeyed($where) { |
|
396 | |||
397 | /** |
||
398 | * Make field list safe. NOT DANGER |
||
399 | * |
||
400 | * This function is NOT DANGER |
||
401 | * Make SQL-safe assignment/equal compare string from (field => value) pair |
||
402 | * |
||
403 | * @param array $fieldValues - array of pair $fieldName => $fieldValue |
||
404 | * |
||
405 | * @return array |
||
406 | */ |
||
407 | 3 | protected function fieldEqValue($fieldValues) { |
|
423 | |||
424 | // INSERT ... VALUES |
||
425 | 3 | View Code Duplication | public function safeFields($fields) { |
439 | |||
440 | 3 | View Code Duplication | public function safeValuesScalar($values) { |
453 | |||
454 | /** |
||
455 | * Make fields adjustment safe. FUNCTION IS NOT DANGER |
||
456 | * |
||
457 | * Convert "key => value" pair to string "`key` = `key` + (value)" |
||
458 | * |
||
459 | * @param array $fields - array of pair $fieldName => $fieldValue |
||
460 | * |
||
461 | * @return array |
||
462 | */ |
||
463 | 3 | View Code Duplication | protected function safeFieldsAdjust($fields) { |
479 | |||
480 | |||
481 | 7 | protected function buildCommand() { |
|
502 | |||
503 | // UPDATE and INSERT ... SET |
||
504 | 1 | protected function buildSetFields() { |
|
526 | |||
527 | // INSERT ... VALUES |
||
528 | 1 | protected function buildFieldNames() { |
|
531 | |||
532 | 1 | protected function buildValuesDanger() { |
|
535 | |||
536 | // ONLY FOR SCALAR VALUES!!! |
||
537 | // NOT DANGER! |
||
538 | 1 | protected function buildValuesScalar() { |
|
541 | |||
542 | /** |
||
543 | * Vector values is for batch INSERT/REPLACE |
||
544 | */ |
||
545 | // TODO - CHECK! |
||
546 | 1 | protected function buildValuesVector() { |
|
555 | |||
556 | |||
557 | 1 | protected function buildWhere() { |
|
570 | |||
571 | 1 | protected function buildLimit() { |
|
576 | |||
577 | |||
578 | 7 | public function __toString() { |
|
581 | |||
582 | } |
||
583 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.