Completed
Push — master ( 49f0f3...ac18ff )
by Lars
05:04
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 7
CRAP Score 3.0987

Importance

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