Completed
Push — master ( ac18ff...92b49e )
by Lars
01:41
created

Helper::repairTables()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 19
Ratio 100 %

Code Coverage

Tests 8
CRAP Score 5.5069

Importance

Changes 0
Metric Value
dl 19
loc 19
ccs 8
cts 11
cp 0.7272
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 11
nc 4
nop 2
crap 5.5069
1
<?php
2
3
namespace voku\db;
4
5
use voku\cache\Cache;
6
use voku\helper\Phonetic;
7
8
/**
9
 * Helper: This class can handle extra functions that use the "Simple-MySQLi"-classes.
10
 *
11
 * @package   voku\db
12
 */
13
class Helper
14
{
15
  /**
16
   * Optimize tables
17
   *
18
   * @param array   $tables database table names
19
   * @param DB|null $dbConnection <p>Use <strong>null</strong> to get your first singleton instance.</p>
20 51
   *
21
   * @return int
22 51
   */
23 View Code Duplication
  public static function optimizeTables(array $tables = array(), 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...
24 51
  {
25 1
    if ($dbConnection === null) {
26 1
      $dbConnection = DB::getInstance();
27
    }
28 51
29
    $optimized = 0;
30
    if (!empty($tables)) {
31
      foreach ($tables as $table) {
32
        $optimize = 'OPTIMIZE TABLE ' . $dbConnection->quote_string($table);
33
        $result = $dbConnection->query($optimize);
34
        if ($result) {
35
          $optimized++;
36
        }
37
      }
38 8
    }
39
40
    return $optimized;
41
  }
42
43
  /**
44
   * Repair tables
45
   *
46
   * @param array $tables database table names
47
   * @param DB|null $dbConnection <p>Use <strong>null</strong> to get your first singleton instance.</p>
48
   *
49
   * @return int
50 8
   */
51 View Code Duplication
  public static function repairTables(array $tables = array(), 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...
52
  {
53
    if ($dbConnection === null) {
54 8
      $dbConnection = DB::getInstance();
55 8
    }
56
57
    $optimized = 0;
58
    if (!empty($tables)) {
59 8
      foreach ($tables as $table) {
60
        $optimize = 'REPAIR TABLE ' . $dbConnection->quote_string($table);
61
        $result = $dbConnection->query($optimize);
62 8
        if ($result) {
63 8
          $optimized++;
64
        }
65 8
      }
66
    }
67
68 8
    return $optimized;
69 8
  }
70
71
  /**
72 8
   * Check if "mysqlnd"-driver is used.
73
   *
74 8
   * @return bool
75
   */
76
  public static function isMysqlndIsUsed()
77
  {
78 8
    static $_mysqlnd_is_used = null;
79
80
    if ($_mysqlnd_is_used === null) {
81
      $_mysqlnd_is_used = (extension_loaded('mysqlnd') && function_exists('mysqli_fetch_all'));
82
    }
83
84
    return $_mysqlnd_is_used;
85
  }
86
87
  /**
88
   * Check if the current environment supports "utf8mb4".
89
   *
90
   * @param DB $dbConnection
91
   *
92
   * @return bool
93
   */
94
  public static function isUtf8mb4Supported(DB $dbConnection = null)
95
  {
96
    /**
97
     *  https://make.wordpress.org/core/2015/04/02/the-utf8mb4-upgrade/
98
     *
99 2
     * - You’re currently using the utf8 character set.
100
     * - Your MySQL server is version 5.5.3 or higher (including all 10.x versions of MariaDB).
101
     * - Your MySQL client libraries are version 5.5.3 or higher. If you’re using mysqlnd, 5.0.9 or higher.
102 2
     *
103 2
     * INFO: utf8mb4 is 100% backwards compatible with utf8.
104 2
     */
105
106 2
    if ($dbConnection === null) {
107 2
      $dbConnection = DB::getInstance();
108 2
    }
109
110 2
    $server_version = self::get_mysql_server_version($dbConnection);
111
    $client_version = self::get_mysql_client_version($dbConnection);
112
113
    if (
114
        $server_version >= 50503
115
        &&
116
        (
117 2
            (
118
                self::isMysqlndIsUsed() === true
119
                &&
120
                $client_version >= 50009
121 2
            )
122 2
            ||
123 2
            (
124 2
                self::isMysqlndIsUsed() === false
125
                &&
126 2
                $client_version >= 50503
127
            )
128
        )
129
130
    ) {
131 2
      return true;
132 2
    }
133
134 2
    return false;
135 2
  }
136
137 2
  /**
138 1
   * A phonetic search algorithms for different languages.
139 1
   *
140
   * INFO: if you need better performance, please save the "voku\helper\Phonetic"-output into the DB and search for it
141
   *
142 1
   * @param string      $searchString
143 1
   * @param string      $searchFieldName
144 1
   * @param string      $idFieldName
145 1
   * @param string      $language <p>en, de, fr</p>
146 1
   * @param string      $table
147
   * @param array       $whereArray
148
   * @param DB|null     $dbConnection <p>use <strong>null</strong> if you will use the current database-connection</p>
149 1
   * @param null|string $databaseName <p>use <strong>null</strong> if you will use the current database</p>
150 1
   * @param bool        $useCache use cache?
151
   * @param int         $cacheTTL cache-ttl in seconds
152
   *
153 2
   * @return array
154
   */
155
  public static function phoneticSearch($searchString, $searchFieldName, $idFieldName = null, $language = 'de', $table, array $whereArray = null, DB $dbConnection = null, $databaseName = null, $useCache = false, $cacheTTL = 3600)
156 2
  {
157
    // init
158
    $cacheKey = null;
159
    $searchString = (string)$searchString;
160 2
    $searchFieldName = (string)$searchFieldName;
161
162
    if ($dbConnection === null) {
163 2
      $dbConnection = DB::getInstance();
164 2
    }
165 2
166 View Code Duplication
    if ($table === '') {
167 2
      $debug = new Debug($dbConnection);
168 2
      $debug->displayError('Invalid table name, table name in empty.', false);
169
170
      return array();
171
    }
172
173 2
    if ($idFieldName === null) {
174
      $idFieldName = 'id';
175 2
    }
176
177 2
    $whereSQL = $dbConnection->_parseArrayPair($whereArray, 'AND');
0 ignored issues
show
Bug introduced by
It seems like $whereArray defined by parameter $whereArray on line 155 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...
178 1
    if ($whereSQL) {
179 2
      $whereSQL = 'AND ' . $whereSQL;
180 1
    }
181 1
182
    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...
183 2
      $databaseName = $dbConnection->quote_string(trim($databaseName)) . '.';
184
    }
185
186
    // get the row
187
    $query = 'SELECT ' . $dbConnection->quote_string($searchFieldName) . ', ' . $dbConnection->quote_string($idFieldName) . ' 
188
      FROM ' . $databaseName . $dbConnection->quote_string($table) . '
189
      WHERE 1 = 1
190
      ' . $whereSQL . '
191
    ';
192
193 8 View Code Duplication
    if ($useCache === true) {
194
      $cache = new Cache(null, null, false, $useCache);
195 8
      $cacheKey = 'sql-phonetic-search-' . md5($query);
196
197 8
      if (
198
          $cache->getCacheIsReady() === true
199
          &&
200
          $cache->existsItem($cacheKey)
201 8
      ) {
202 1
        return $cache->getItem($cacheKey);
203 1
      }
204
205 8
    } else {
206
      $cache = false;
207
    }
208
209
    $result = $dbConnection->query($query);
210
211
    // make sure the row exists
212
    if ($result->num_rows <= 0) {
213
      return array();
214
    }
215
216 8
    $dataToSearchIn = array();
217
    /** @noinspection LoopWhichDoesNotLoopInspection */
218 8
    /** @noinspection PhpAssignmentInConditionInspection */
219
    while ($tmpArray = $result->fetchArray()) {
220 8
      $dataToSearchIn[$tmpArray[$idFieldName]] = $tmpArray[$searchFieldName];
221
    }
222
223
    $phonetic = new Phonetic($language);
224 8
    $return = $phonetic->phonetic_matches($searchString, $dataToSearchIn);
225 1
226 1
    // save into the cache
227 View Code Duplication
    if (
228 8
        $cacheKey !== null
229
        &&
230
        $useCache === true
231
        &&
232
        $cache instanceof Cache
233
        &&
234
        $cache->getCacheIsReady() === true
235
    ) {
236
      $cache->setItem($cacheKey, $return, $cacheTTL);
237
    }
238
239
    return $return;
240
  }
241 1
242
  /**
243 1
   * A string that represents the MySQL client library version.
244
   *
245
   * @param DB $dbConnection
246
   *
247
   * @return string
248 1
   */
249 1 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...
250 1
  {
251 1
    static $_mysqli_client_version = null;
252
253
    if ($dbConnection === null) {
254
      $dbConnection = DB::getInstance();
255 1
    }
256
257 1
    if ($_mysqli_client_version === null) {
258 1
      $_mysqli_client_version = \mysqli_get_client_version($dbConnection->getLink());
259 1
    }
260
261 1
    return $_mysqli_client_version;
262
  }
263
264
265
  /**
266
   * Returns a string representing the version of the MySQL server that the MySQLi extension is connected to.
267
   *
268 1
   * @param DB $dbConnection
269
   *
270
   * @return string
271
   */
272 1 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...
273 1
  {
274
    static $_mysqli_server_version = null;
275 1
276 1
    if ($dbConnection === null) {
277 1
      $dbConnection = DB::getInstance();
278 1
    }
279 1
280
    if ($_mysqli_server_version === null) {
281
      $_mysqli_server_version = \mysqli_get_server_version($dbConnection->getLink());
282 1
    }
283
284 1
    return $_mysqli_server_version;
285
  }
286
287
  /**
288
   * Return all db-fields from a table.
289
   *
290
   * @param string      $table
291
   * @param bool        $useStaticCache
292
   * @param DB|null     $dbConnection <p>use <strong>null</strong> if you will use the current database-connection</p>
293
   * @param null|string $databaseName <p>use <strong>null</strong> if you will use the current database</p>
294
   *
295
   * @return array
296
   */
297
  public static function getDbFields($table, $useStaticCache = true, DB $dbConnection = null, $databaseName = null)
298
  {
299
    static $DB_FIELDS_CACHE = array();
300 1
301
    // use the static cache
302
    if (
303 1
        $useStaticCache === true
304
        &&
305 1
        isset($DB_FIELDS_CACHE[$table])
306 1
    ) {
307 1
      return $DB_FIELDS_CACHE[$table];
308
    }
309 1
310
    // init
311
    $dbFields = array();
312
313
    if ($dbConnection === null) {
314
      $dbConnection = DB::getInstance();
315
    }
316 1
317 1 View Code Duplication
    if ($table === '') {
318 1
      $debug = new Debug($dbConnection);
319 1
      $debug->displayError('Invalid table name, table name in empty.', false);
320
321 1
      return array();
322
    }
323
324
    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...
325
      $databaseName = $dbConnection->quote_string(trim($databaseName)) . '.';
326 1
    }
327
328 1
    $sql = 'SHOW COLUMNS FROM ' . $databaseName . $dbConnection->escape($table);
329 1
    $result = $dbConnection->query($sql);
330 1
331
    if ($result && $result->num_rows > 0) {
332
      foreach ($result->fetchAllArray() as $tmpResult) {
333 1
        $dbFields[] = $tmpResult['Field'];
334
      }
335
    }
336
337 1
    // add to static cache
338
    $DB_FIELDS_CACHE[$table] = $dbFields;
339
340 1
    return $dbFields;
341 1
  }
342 1
343
  /**
344 1
   * Copy row within a DB table and making updates to the columns.
345
   *
346 1
   * @param string  $table
347 1
   * @param array   $whereArray
348 1
   * @param array   $updateArray
349 1
   * @param array   $ignoreArray
350 1
   * @param DB|null $dbConnection <p>Use <strong>null</strong> to get your first singleton instance.</p>
351 1
   * @param null|string $databaseName <p>use <strong>null</strong> if you will use the current database</p>
352 1
   *
353 1
   * @return bool|int "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
354 1
   *                   "false" on error
355
   */
356 1
  public static function copyTableRow($table, array $whereArray, array $updateArray = array(), array $ignoreArray = array(), DB $dbConnection = null, $databaseName = null)
357 1
  {
358
    // init
359 1
    $table = trim($table);
360 1
361
    if ($dbConnection === null) {
362
      $dbConnection = DB::getInstance();
363 1
    }
364 1
365 View Code Duplication
    if ($table === '') {
366 1
      $debug = new Debug($dbConnection);
367 1
      $debug->displayError('Invalid table name, table name in empty.', false);
368 1
369
      return false;
370
    }
371
372
    $whereSQL = $dbConnection->_parseArrayPair($whereArray, 'AND');
373
    if ($whereSQL) {
374
      $whereSQL = 'AND ' . $whereSQL;
375
    }
376
377
    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...
378
      $databaseName = $dbConnection->quote_string(trim($databaseName)) . '.';
379
    }
380
381
    // get the row
382
    $query = 'SELECT * FROM ' . $databaseName . $dbConnection->quote_string($table) . '
383
      WHERE 1 = 1
384
      ' . $whereSQL . '
385
    ';
386
    $result = $dbConnection->query($query);
387
388
    // make sure the row exists
389
    if ($result->num_rows > 0) {
390
391
      /** @noinspection LoopWhichDoesNotLoopInspection */
392
      /** @noinspection PhpAssignmentInConditionInspection */
393
      while ($tmpArray = $result->fetchArray()) {
394
395
        // re-build a new DB query and ignore some field-names
396
        $bindings = array();
397
        $insert_keys = '';
398
        $insert_values = '';
399
400
        foreach ($tmpArray as $fieldName => $value) {
401
402
          if (!in_array($fieldName, $ignoreArray, true)) {
403
            if (array_key_exists($fieldName, $updateArray)) {
404
              $insert_keys .= ',' . $fieldName;
405
              $insert_values .= ',?';
406
              $bindings[] = $updateArray[$fieldName]; // INFO: do not escape non selected data
407
            } else {
408
              $insert_keys .= ',' . $fieldName;
409
              $insert_values .= ',?';
410
              $bindings[] = $value; // INFO: do not escape non selected data
411
            }
412
          }
413
        }
414
415
        $insert_keys = ltrim($insert_keys, ',');
416
        $insert_values = ltrim($insert_values, ',');
417
418
        // insert the "copied" row
419
        $new_query = 'INSERT INTO ' . $databaseName . $dbConnection->quote_string($table) . ' 
420
          (' . $insert_keys . ')
421
          VALUES 
422
          (' . $insert_values . ')
423
        ';
424
        return $dbConnection->query($new_query, $bindings);
425
      }
426
    }
427
428
    return false;
429
  }
430
}
431