Completed
Push — master ( a781c6...24e936 )
by Lars
06:09
created

Helper::get_mysql_client_version()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 14
Code Lines 7

Duplication

Lines 14
Ratio 100 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

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