Completed
Push — work-fleets ( f3291e...1beb80 )
by SuperNova.WS
05:58
created

DbQuery   C

Complexity

Total Complexity 60

Size/Duplication

Total Lines 449
Duplicated Lines 10.91 %

Coupling/Cohesion

Components 1
Dependencies 4

Test Coverage

Coverage 98.95%

Importance

Changes 7
Bugs 0 Features 1
Metric Value
dl 49
loc 449
rs 6.0975
c 7
b 0
f 1
ccs 188
cts 190
cp 0.9895
wmc 60
lcom 1
cbo 4

30 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 3 2
A build() 0 3 1
A delete() 10 10 1
A update() 11 11 1
A insertSet() 11 20 3
A insertBatch() 11 23 3
A setTable() 0 5 1
A setOneRow() 0 5 1
A setValues() 0 5 1
A setValuesDanger() 0 5 1
A setAdjust() 0 5 1
A setAdjustDanger() 0 5 1
A setFields() 0 5 1
A setWhereArray() 0 5 1
A setWhereArrayDanger() 0 5 1
A escape() 0 3 1
A escapeEmulator() 0 8 1
A stringValue() 0 3 1
A quote() 0 3 1
A makeAdjustString() 0 6 2
A makeFieldEqualValue() 0 5 2
A quoteTable() 0 3 1
B buildCommand() 6 21 7
B buildSetFields() 0 22 6
A buildFieldNames() 0 3 1
D castAsDbValue() 0 29 9
A buildValuesVector() 0 13 3
A buildWhere() 0 11 2
A buildLimit() 0 5 2
A __toString() 0 3 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

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 Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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
2
/**
3
 * Created by Gorlum 07.08.2016 2:36
4
 */
5
6
namespace DBAL;
7
8
use \HelperArray;
9
use \db_mysql;
10
use \classSupernova;
11
12
/**
13
 * Class DbQuery
14
 *
15
 * New replacement for DbQueryConstructor
16
 * Simplified version
17
 * Chained calls - "Fluid interface"
18
 *
19
 * @package DBAL
20
 */
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) {
91 1
    $this->db = empty($db) ? classSupernova::$gc->db : $db;
92 1
  }
93
94
  /**
95
   * @param null|db_mysql $db
96
   *
97
   * @return static
98
   */
99 1
  public static function build($db = null) {
100 1
    return new static($db);
101
  }
102
103
104 1 View Code Duplication
  public function delete() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
105 1
    $this->build = array();
106
107 1
    $this->command = static::DELETE;
108 1
    $this->buildCommand();
109 1
    $this->buildWhere();
110 1
    $this->buildLimit();
111
112 1
    return implode('', $this->build);
113
  }
114
115 1 View Code Duplication
  public function update() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
116 1
    $this->build = array();
117
118 1
    $this->command = static::UPDATE;
119 1
    $this->buildCommand();
120 1
    $this->buildSetFields();
121 1
    $this->buildWhere();
122 1
    $this->buildLimit();
123
124 1
    return implode('', $this->build);
125
  }
126
127 1
  public function insertSet($replace) {
128 1
    $this->build = array();
129
130 View Code Duplication
    switch ($replace) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
131 1
      case DB_INSERT_IGNORE:
132 1
        $this->command = static::INSERT_IGNORE;
133 1
      break;
134 1
      case DB_INSERT_REPLACE:
135 1
        $this->command = static::REPLACE;
136 1
      break;
137 1
      default:
138 1
        $this->command = static::INSERT;
139 1
      break;
140 1
    }
141
142 1
    $this->buildCommand();
143 1
    $this->buildSetFields();
144
145 1
    return implode('', $this->build);
146
  }
147
148 1
  public function insertBatch($replace) {
149 1
    $this->build = array();
150
151 View Code Duplication
    switch ($replace) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
152 1
      case DB_INSERT_IGNORE:
153 1
        $this->command = static::INSERT_IGNORE;
154 1
      break;
155 1
      case DB_INSERT_REPLACE:
156 1
        $this->command = static::REPLACE;
157 1
      break;
158 1
      default:
159 1
        $this->command = static::INSERT;
160 1
      break;
161 1
    }
162
163 1
    $this->buildCommand();
164 1
    $this->build[] = " (";
165 1
    $this->buildFieldNames();
166 1
    $this->build[] = ") VALUES ";
167 1
    $this->buildValuesVector();
168
169 1
    return implode('', $this->build);
170
  }
171
172
173
  /**
174
   * @param $table
175
   *
176
   * @return $this
177
   */
178 1
  public function setTable($table) {
179 1
    $this->table = $table;
180
181 1
    return $this;
182
  }
183
184
  /**
185
   * @param bool $oneRow - DB_RECORDS_ALL || DB_RECORD_ONE
186
   *
187
   * @return $this
188
   */
189 1
  public function setOneRow($oneRow = DB_RECORDS_ALL) {
190 1
    $this->isOneRow = $oneRow;
191
192 1
    return $this;
193
  }
194
195
  /**
196
   * @param array $values
197
   *
198
   * @return $this
199
   */
200 1
  public function setValues($values = array()) {
201 1
    HelperArray::merge($this->values, $values, HelperArray::MERGE_PHP);
202
203 1
    return $this;
204
  }
205
206
  /**
207
   * @param array $values
208
   *
209
   * @return $this
210
   */
211 1
  public function setValuesDanger($values = array()) {
212 1
    HelperArray::merge($this->valuesDanger, $values, HelperArray::MERGE_PHP);
213
214 1
    return $this;
215
  }
216
217
  /**
218
   * @param array $values
219
   *
220
   * @return $this
221
   */
222 1
  public function setAdjust($values = array()) {
223 1
    HelperArray::merge($this->adjust, $values, HelperArray::MERGE_PHP);
224
225 1
    return $this;
226
  }
227
228
  /**
229
   * @param array $values
230
   *
231
   * @return $this
232
   */
233 1
  public function setAdjustDanger($values = array()) {
234 1
    HelperArray::merge($this->adjustDanger, $values, HelperArray::MERGE_PHP);
235
236 1
    return $this;
237
  }
238
239
  /**
240
   * @param array $fields
241
   *
242
   * @return $this
243
   */
244 1
  public function setFields($fields = array()) {
245 1
    HelperArray::merge($this->fields, $fields, HelperArray::MERGE_PHP);
246
247 1
    return $this;
248
  }
249
250
  /**
251
   * Merges WHERE array as array_merge()
252
   *
253
   * @param array $whereArray
254
   *
255
   * @return $this
256
   */
257 1
  public function setWhereArray($whereArray = array()) {
258 1
    HelperArray::merge($this->where, $whereArray, HelperArray::MERGE_PHP);
259
260 1
    return $this;
261
  }
262
263
  /**
264
   * Sets DANGER array - where values should be escaped BEFORE entering DBAL
265
   *
266
   * Deprecated - all values should pass through DBAL
267
   *
268
   * @param array $whereArrayDanger
269
   *
270
   * @return $this
271
   */
272 1
  public function setWhereArrayDanger($whereArrayDanger = array()) {
273 1
    HelperArray::merge($this->whereDanger, $whereArrayDanger, HelperArray::MERGE_PHP);
274
275 1
    return $this;
276
  }
277
278
279
  /**
280
   * Wrapper for db_escape()
281
   *
282
   * @param $string
283
   *
284
   * @return string
285
   */
286 1
  protected function escape($string) {
287 1
    return $this->db->db_escape($string);
288
  }
289
290 1
  protected function escapeEmulator($value) {
291
    // Characters encoded are NUL (ASCII 0), \n, \r, \, ', ", and Control-Z.
292 1
    return str_replace(
293 1
      array("\\", "\0", "\n", "\r", "'", "\"", "\z",),
294 1
      array('\\\\', '\0', '\n', '\r', '\\\'', '\"', '\z',),
295
      $value
296 1
    );
297
  }
298
299
  /**
300
   * Escaping string value and quoting it
301
   *
302
   * @param mixed $value
303
   *
304
   * @return string
305
   */
306 1
  protected function stringValue($value) {
307 1
    return "'" . $this->escape((string)$value) . "'";
308
  }
309
310
  /**
311
   * Quote mysql DB identifier
312
   *
313
   * @param mixed $fieldName
314
   *
315
   * @return string
316
   */
317 1
  public function quote($fieldName) {
318 1
    return "`" . $this->escape((string)$fieldName) . "`";
319
  }
320
321 1
  public function makeAdjustString($fieldValue, $fieldName) {
322 1
    return is_int($fieldName)
323 1
      ? $fieldValue
324 1
      : (($fieldNameQuoted = $this->quote($fieldName)) . " = " .
325 1
        $fieldNameQuoted . " + (" . $this->castAsDbValue($fieldValue) . ")");
326
  }
327
328 1
  public function makeFieldEqualValue($fieldValue, $fieldName) {
329 1
    return is_int($fieldName)
330 1
      ? $fieldValue
331 1
      : ($this->quote($fieldName) . " = " . $this->castAsDbValue($fieldValue));
332
  }
333
334
  /**
335
   * Quote table name with `{{ }}`
336
   *
337
   * @param mixed $tableName
338
   *
339
   * @return string
340
   */
341 1
  protected function quoteTable($tableName) {
342 1
    return "`{{" . $this->escape((string)$tableName) . "}}`";
343
  }
344
345 13
  public function castAsDbValue($value) {
346 13
    switch (gettype($value)) {
347 13
      case TYPE_INTEGER:
348 13
      case TYPE_DOUBLE:
349
        // do nothing
350 4
      break;
351
352 9
      case TYPE_BOOLEAN:
353 2
        $value = $value ? 1 : 0;
354 2
      break;
355
356 7
      case TYPE_NULL:
357 1
        $value = 'NULL';
358 1
      break;
359
360 6
      case TYPE_EMPTY:
361
        // No-type defaults to string
362
        /** @noinspection PhpMissingBreakStatementInspection */
363 6
      case TYPE_ARRAY:
364 2
        $value = serialize($value);
365
      // Continuing with serialized array value
366 6
      case TYPE_STRING:
367 6
      default:
368 6
        $value = $this->stringValue($value);
369 6
      break;
370 13
    }
371
372 13
    return $value;
373
  }
374
375
376 7
  protected function buildCommand() {
377 7
    switch ($this->command) {
378 7 View Code Duplication
      case static::UPDATE:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
379 1
        $this->build[] = $this->command . " " . $this->quoteTable($this->table);
380 1
      break;
381
382 6 View Code Duplication
      case static::DELETE:
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
383 1
        $this->build[] = $this->command . " FROM " . $this->quoteTable($this->table);
384 1
      break;
385
386 5
      case static::REPLACE:
387 5
      case static::INSERT_IGNORE:
388 5
      case static::INSERT:
389 3
        $this->build[] = $this->command . " INTO " . $this->quoteTable($this->table);
390 3
      break;
391
392 2
      case static::SELECT:
393 1
        $this->build[] = $this->command . " ";
394 1
      break;
395 7
    }
396 7
  }
397
398
  // UPDATE and INSERT ... SET
399 1
  protected function buildSetFields() {
400 1
    $safeFields = array();
401
    // Sets overwritten by Adjusts
402 1
    if ($safeValuesDanger = implode(',', $this->valuesDanger)) {
403 1
      $safeFields[] = &$safeValuesDanger;
404 1
    }
405 1
    if ($safeFieldsEqualValues = implode(',', HelperArray::map($this->values, array($this, 'makeFieldEqualValue'), true))) {
406 1
      $safeFields[] = &$safeFieldsEqualValues;
407 1
    }
408 1
    if ($safeAdjustDanger = implode(',', $this->adjustDanger)) {
409 1
      $safeFields[] = &$safeAdjustDanger;
410 1
    }
411 1
    if ($safeAdjust = implode(',', HelperArray::map($this->adjust, array($this, 'makeAdjustString'), true))) {
412 1
      $safeFields[] = &$safeAdjust;
413 1
    }
414 1
    $safeFieldsString = implode(',', $safeFields);
415
416 1
    if (!empty($safeFieldsString)) {
417 1
      $this->build[] = ' SET ';
418 1
      $this->build[] = $safeFieldsString;
419 1
    }
420 1
  }
421
422
  // INSERT ... VALUES
423 1
  protected function buildFieldNames() {
424 1
    $this->build[] = implode(',', HelperArray::map($this->fields, array($this, 'quote')));
425 1
  }
426
427
  /**
428
   * Vector values is for batch INSERT/REPLACE
429
   */
430
  // TODO - CHECK!
431 1
  protected function buildValuesVector() {
432 1
    $compiled = array();
433
434 1
    if(!empty($this->valuesDanger)) {
435
      $compiled = $this->valuesDanger;
436
    }
437
438 1
    foreach ($this->values as $valuesVector) {
439 1
      $compiled[] = '(' . implode(',', HelperArray::map($valuesVector, array($this, 'castAsDbValue'))) . ')';
440 1
    }
441
442 1
    $this->build[] = implode(',', $compiled);
443 1
  }
444
445
446 1
  protected function buildWhere() {
447 1
    $safeWhere = implode(
448 1
      ' AND ',
449 1
      $this->whereDanger +
450 1
      HelperArray::map($this->where, array($this, 'makeFieldEqualValue'), true)
451 1
    );
452
453 1
    if (!empty($safeWhere)) {
454 1
      $this->build[] = " WHERE {$safeWhere}";
455 1
    }
456 1
  }
457
458 1
  protected function buildLimit() {
459 1
    if ($this->isOneRow == DB_RECORD_ONE) {
460 1
      $this->build[] = ' LIMIT 1';
461 1
    }
462 1
  }
463
464
465 7
  public function __toString() {
466 7
    return implode('', $this->build);
467
  }
468
469
}
470