Completed
Push — master ( b96847...7d79e5 )
by Lars
24:52 queued 11:30
created

Helper   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 327
Duplicated Lines 8.56 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 78.23%

Importance

Changes 0
Metric Value
wmc 43
lcom 1
cbo 3
dl 28
loc 327
ccs 115
cts 147
cp 0.7823
rs 8.3157
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A isMysqlndIsUsed() 0 10 3
C isUtf8mb4Supported() 0 42 7
B phoneticSearch() 0 53 8
A get_mysql_client_version() 14 14 3
A get_mysql_server_version() 14 14 3
D getDbFields() 0 45 9
C copyTableRow() 0 74 10

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 Helper 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 Helper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace voku\db;
4
5
use voku\helper\Phonetic;
6
7
/**
8
 * Helper: this handles extra functions that use the "DB"-Class
9
 *
10
 * @package   voku\db
11
 */
12
class Helper
13
{
14
  /**
15
   * Check if "mysqlnd"-driver is used.
16
   *
17
   * @return bool
18
   */
19 35
  public static function isMysqlndIsUsed()
20
  {
21 35
    static $_mysqlnd_is_used = null;
22
23 35
    if ($_mysqlnd_is_used === null) {
24 1
      $_mysqlnd_is_used = (extension_loaded('mysqlnd') && function_exists('mysqli_fetch_all'));
25 1
    }
26
27 35
    return $_mysqlnd_is_used;
28
  }
29
30
  /**
31
   * Check if the current environment supports "utf8mb4".
32
   *
33
   * @param DB $dbConnection
34
   *
35
   * @return bool
36
   */
37 5
  public static function isUtf8mb4Supported(DB $dbConnection = null)
38
  {
39
    /**
40
     *  https://make.wordpress.org/core/2015/04/02/the-utf8mb4-upgrade/
41
     *
42
     * - You’re currently using the utf8 character set.
43
     * - Your MySQL server is version 5.5.3 or higher (including all 10.x versions of MariaDB).
44
     * - Your MySQL client libraries are version 5.5.3 or higher. If you’re using mysqlnd, 5.0.9 or higher.
45
     *
46
     * INFO: utf8mb4 is 100% backwards compatible with utf8.
47
     */
48
49 5
    if ($dbConnection === null) {
50
      $dbConnection = DB::getInstance();
51
    }
52
53 5
    $server_version = self::get_mysql_server_version($dbConnection);
54 5
    $client_version = self::get_mysql_client_version($dbConnection);
55
56
    if (
57
        $server_version >= 50503
58 5
        &&
59
        (
60
            (
61 5
                self::isMysqlndIsUsed() === true
62 5
                &&
63
                $client_version >= 50009
64 5
            )
65
            ||
66
            (
67
                self::isMysqlndIsUsed() === false
68
                &&
69
                $client_version >= 50503
70
            )
71
        )
72
73 5
    ) {
74 5
      return true;
75
    }
76
77
    return false;
78
  }
79
80
  /**
81
   * A phonetic search algorithms for different languages.
82
   *
83
   * INFO: if you need better performance, please save the "voku\helper\Phonetic"-output into the DB and search for it
84
   *
85
   * @param string      $searchString
86
   * @param string      $searchFieldName
87
   * @param string      $idFieldName
88
   * @param string      $language <p>en, de, fr</p>
89
   * @param string      $table
90
   * @param array       $whereArray
91
   * @param DB|null     $dbConnection <p>use <strong>null</strong> if you will use the current database-connection</p>
92
   * @param null|string $databaseName <p>use <strong>null</strong> if you will use the current database</p>
93
   *
94
   * @return array
95
   */
96 1
  public static function phoneticSearch($searchString, $searchFieldName, $idFieldName = null, $language = 'de', $table, array $whereArray = null, DB $dbConnection = null, $databaseName = null)
97
  {
98
    // init
99 1
    $searchString = (string)$searchString;
100 1
    $searchFieldName = (string)$searchFieldName;
101
102 1
    if ($dbConnection === null) {
103 1
      $dbConnection = DB::getInstance();
104 1
    }
105
106 1
    if ($table === '') {
107
      $debug = new Debug($dbConnection);
108
      $debug->displayError('invalid table name');
109
110
      return array();
111
    }
112
113 1
    if ($idFieldName === null) {
114
      $idFieldName = 'id';
115
    }
116
117 1
    $whereSQL = $dbConnection->_parseArrayPair($whereArray, 'AND');
0 ignored issues
show
Bug introduced by
It seems like $whereArray defined by parameter $whereArray on line 96 can also be of type null; however, voku\db\DB::_parseArrayPair() does only seem to accept array, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
118 1
    if ($whereSQL) {
119 1
      $whereSQL = 'AND ' . $whereSQL;
120 1
    }
121
122 1
    if ($databaseName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $databaseName of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
123
      $databaseName = $dbConnection->quote_string(trim($databaseName)) . '.';
124
    }
125
126
    // get the row
127 1
    $query = 'SELECT ' . $dbConnection->quote_string($searchFieldName) . ', ' . $dbConnection->quote_string($idFieldName) . ' 
128 1
      FROM ' . $databaseName . $dbConnection->quote_string($table) . '
129
      WHERE 1 = 1
130 1
      ' . $whereSQL . '
131 1
    ';
132 1
    $result = $dbConnection->query($query);
133
134
    // make sure the row exists
135 1
    if ($result->num_rows <= 0) {
136
      return array();
137
    }
138
139 1
    $dataToSearchIn = array();
140
    /** @noinspection LoopWhichDoesNotLoopInspection */
141
    /** @noinspection PhpAssignmentInConditionInspection */
142 1
    while ($tmpArray = $result->fetchArray()) {
143 1
      $dataToSearchIn[$tmpArray[$idFieldName]] = $tmpArray[$searchFieldName];
144 1
    }
145
146 1
    $phonetic = new Phonetic($language);
147 1
    return $phonetic->phonetic_matches($searchString, $dataToSearchIn);
148
  }
149
150
  /**
151
   * A string that represents the MySQL client library version.
152
   *
153
   * @param DB $dbConnection
154
   *
155
   * @return string
156
   */
157 5 View Code Duplication
  public static function get_mysql_client_version(DB $dbConnection = null)
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...
158
  {
159 5
    static $_mysqli_client_version = null;
160
161 5
    if ($dbConnection === null) {
162
      $dbConnection = DB::getInstance();
163
    }
164
165 5
    if ($_mysqli_client_version === null) {
166 1
      $_mysqli_client_version = \mysqli_get_client_version($dbConnection->getLink());
167 1
    }
168
169 5
    return $_mysqli_client_version;
170
  }
171
172
173
  /**
174
   * Returns a string representing the version of the MySQL server that the MySQLi extension is connected to.
175
   *
176
   * @param DB $dbConnection
177
   *
178
   * @return string
179
   */
180 5 View Code Duplication
  public static function get_mysql_server_version(DB $dbConnection = null)
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...
181
  {
182 5
    static $_mysqli_server_version = null;
183
184 5
    if ($dbConnection === null) {
185
      $dbConnection = DB::getInstance();
186
    }
187
188 5
    if ($_mysqli_server_version === null) {
189 1
      $_mysqli_server_version = \mysqli_get_server_version($dbConnection->getLink());
190 1
    }
191
192 5
    return $_mysqli_server_version;
193
  }
194
195
  /**
196
   * Return all db-fields from a table.
197
   *
198
   * @param string      $table
199
   * @param bool        $useStaticCache
200
   * @param DB|null     $dbConnection <p>use <strong>null</strong> if you will use the current database-connection</p>
201
   * @param null|string $databaseName <p>use <strong>null</strong> if you will use the current database</p>
202
   *
203
   * @return array
204
   */
205 1
  public static function getDbFields($table, $useStaticCache = true, DB $dbConnection = null, $databaseName = null)
206
  {
207 1
    static $DB_FIELDS_CACHE = array();
208
209
    // use the static cache
210
    if (
211
        $useStaticCache === true
212 1
        &&
213 1
        isset($DB_FIELDS_CACHE[$table])
214 1
    ) {
215 1
      return $DB_FIELDS_CACHE[$table];
216
    }
217
218
    // init
219 1
    $dbFields = array();
220
221 1
    if ($dbConnection === null) {
222 1
      $dbConnection = DB::getInstance();
223 1
    }
224
225 1
    if ($table === '') {
226
      $debug = new Debug($dbConnection);
227
      $debug->displayError('invalid table name');
228
229
      return array();
230
    }
231
232 1
    if ($databaseName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $databaseName of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
233
      $databaseName = $dbConnection->quote_string(trim($databaseName)) . '.';
234
    }
235
236 1
    $sql = 'SHOW COLUMNS FROM ' . $databaseName . $dbConnection->escape($table);
237 1
    $result = $dbConnection->query($sql);
238
239 1
    if ($result && $result->num_rows > 0) {
240 1
      foreach ($result->fetchAllArray() as $tmpResult) {
241 1
        $dbFields[] = $tmpResult['Field'];
242 1
      }
243 1
    }
244
245
    // add to static cache
246 1
    $DB_FIELDS_CACHE[$table] = $dbFields;
247
248 1
    return $dbFields;
249
  }
250
251
  /**
252
   * Copy row within a DB table and making updates to the columns.
253
   *
254
   * @param string  $table
255
   * @param array   $whereArray
256
   * @param array   $updateArray
257
   * @param array   $ignoreArray
258
   * @param DB|null $dbConnection <p>Use <strong>null</strong> to get your first singleton instance.</p>
259
   * @param null|string $databaseName <p>use <strong>null</strong> if you will use the current database</p>
260
   *
261
   * @return bool|int "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
262
   *                   "false" on error
263
   */
264 1
  public static function copyTableRow($table, array $whereArray, array $updateArray = array(), array $ignoreArray = array(), DB $dbConnection = null, $databaseName = null)
265
  {
266
    // init
267 1
    $table = trim($table);
268
269 1
    if ($dbConnection === null) {
270 1
      $dbConnection = DB::getInstance();
271 1
    }
272
273 1
    if ($table === '') {
274
      $debug = new Debug($dbConnection);
275
      $debug->displayError('invalid table name');
276
277
      return false;
278
    }
279
280 1
    $whereSQL = $dbConnection->_parseArrayPair($whereArray, 'AND');
281 1
    if ($whereSQL) {
282 1
      $whereSQL = 'AND ' . $whereSQL;
283 1
    }
284
285 1
    if ($databaseName) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $databaseName of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
286
      $databaseName = $dbConnection->quote_string(trim($databaseName)) . '.';
287
    }
288
289
    // get the row
290 1
    $query = 'SELECT * FROM ' . $databaseName . $dbConnection->quote_string($table) . '
291
      WHERE 1 = 1
292 1
      ' . $whereSQL . '
293 1
    ';
294 1
    $result = $dbConnection->query($query);
295
296
    // make sure the row exists
297 1
    if ($result->num_rows > 0) {
298
299
      /** @noinspection LoopWhichDoesNotLoopInspection */
300
      /** @noinspection PhpAssignmentInConditionInspection */
301 1
      while ($tmpArray = $result->fetchArray()) {
302
303
        // re-build a new DB query and ignore some field-names
304 1
        $bindings = array();
305 1
        $insert_keys = '';
306 1
        $insert_values = '';
307
308 1
        foreach ($tmpArray as $fieldName => $value) {
309
310 1
          if (!in_array($fieldName, $ignoreArray, true)) {
311 1
            if (array_key_exists($fieldName, $updateArray)) {
312 1
              $insert_keys .= ',' . $fieldName;
313 1
              $insert_values .= ',?';
314 1
              $bindings[] = $updateArray[$fieldName]; // INFO: do not escape non selected data
315 1
            } else {
316 1
              $insert_keys .= ',' . $fieldName;
317 1
              $insert_values .= ',?';
318 1
              $bindings[] = $value; // INFO: do not escape non selected data
319
            }
320 1
          }
321 1
        }
322
323 1
        $insert_keys = ltrim($insert_keys, ',');
324 1
        $insert_values = ltrim($insert_values, ',');
325
326
        // insert the "copied" row
327 1
        $new_query = 'INSERT INTO ' . $databaseName . $dbConnection->quote_string($table) . ' 
328 1
          (' . $insert_keys . ')
329
          VALUES 
330 1
          (' . $insert_values . ')
331 1
        ';
332 1
        return $dbConnection->query($new_query, $bindings);
333
      }
334
    }
335
336
    return false;
337
  }
338
}
339