Completed
Push — master ( 9bbc25...52f1a7 )
by Lars
02:17
created

Helper::isMysqlndIsUsed()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 10
ccs 5
cts 5
cp 1
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 3
nop 0
crap 3
1
<?php
2
3
namespace voku\db;
4
5
use voku\cache\Cache;
6
use voku\helper\Phonetic;
7
8
/**
9
 * Helper: this handles extra functions that use the "DB"-Class
10
 *
11
 * @package   voku\db
12
 */
13
class Helper
14
{
15
  /**
16
   * Check if "mysqlnd"-driver is used.
17
   *
18
   * @return bool
19 35
   */
20
  public static function isMysqlndIsUsed()
21 35
  {
22
    static $_mysqlnd_is_used = null;
23 35
24 1
    if ($_mysqlnd_is_used === null) {
25 1
      $_mysqlnd_is_used = (extension_loaded('mysqlnd') && function_exists('mysqli_fetch_all'));
26
    }
27 35
28
    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 5
   */
38
  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 5
50
    if ($dbConnection === null) {
51
      $dbConnection = DB::getInstance();
52
    }
53 5
54 5
    $server_version = self::get_mysql_server_version($dbConnection);
55
    $client_version = self::get_mysql_client_version($dbConnection);
56
57
    if (
58 5
        $server_version >= 50503
59
        &&
60
        (
61 5
            (
62 5
                self::isMysqlndIsUsed() === true
63
                &&
64 5
                $client_version >= 50009
65
            )
66
            ||
67
            (
68
                self::isMysqlndIsUsed() === false
69
                &&
70
                $client_version >= 50503
71
            )
72
        )
73 5
74 5
    ) {
75
      return true;
76
    }
77
78
    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 1
   *
97
   * @return array
98
   */
99 1
  public static function phoneticSearch($searchString, $searchFieldName, $idFieldName = null, $language = 'de', $table, array $whereArray = null, DB $dbConnection = null, $databaseName = null, $useCache = false, $cacheTTL = 3600)
100 1
  {
101
    // init
102 1
    $cacheKey = null;
103 1
    $searchString = (string)$searchString;
104 1
    $searchFieldName = (string)$searchFieldName;
105
106 1
    if ($dbConnection === null) {
107
      $dbConnection = DB::getInstance();
108
    }
109
110
    if ($table === '') {
111
      $debug = new Debug($dbConnection);
112
      $debug->displayError('invalid table name');
113 1
114
      return array();
115
    }
116
117 1
    if ($idFieldName === null) {
118 1
      $idFieldName = 'id';
119 1
    }
120 1
121
    $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 1
    if ($whereSQL) {
123
      $whereSQL = 'AND ' . $whereSQL;
124
    }
125
126
    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 1
      $databaseName = $dbConnection->quote_string(trim($databaseName)) . '.';
128 1
    }
129
130 1
    // get the row
131 1
    $query = 'SELECT ' . $dbConnection->quote_string($searchFieldName) . ', ' . $dbConnection->quote_string($idFieldName) . ' 
132 1
      FROM ' . $databaseName . $dbConnection->quote_string($table) . '
133
      WHERE 1 = 1
134
      ' . $whereSQL . '
135 1
    ';
136
137 View Code Duplication
    if ($useCache === true) {
138
      $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
      ) {
146 1
        return $cache->getItem($cacheKey);
147 1
      }
148
149
    } else {
150
      $cache = false;
151
    }
152
153
    $result = $dbConnection->query($query);
154
155
    // make sure the row exists
156
    if ($result->num_rows <= 0) {
157 5
      return array();
158
    }
159 5
160
    $dataToSearchIn = array();
161 5
    /** @noinspection LoopWhichDoesNotLoopInspection */
162
    /** @noinspection PhpAssignmentInConditionInspection */
163
    while ($tmpArray = $result->fetchArray()) {
164
      $dataToSearchIn[$tmpArray[$idFieldName]] = $tmpArray[$searchFieldName];
165 5
    }
166 1
167 1
    $phonetic = new Phonetic($language);
168
    $return = $phonetic->phonetic_matches($searchString, $dataToSearchIn);
169 5
170
    // save into the cache
171 View Code Duplication
    if (
172
        $cacheKey !== null
173
        &&
174
        $useCache === true
175
        &&
176
        $cache instanceof Cache
177
        &&
178
        $cache->getCacheIsReady() === true
179
    ) {
180 5
      $cache->setItem($cacheKey, $return, $cacheTTL);
181
    }
182 5
183
    return $return;
184 5
  }
185
186
  /**
187
   * A string that represents the MySQL client library version.
188 5
   *
189 1
   * @param DB $dbConnection
190 1
   *
191
   * @return string
192 5
   */
193 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
    static $_mysqli_client_version = null;
196
197
    if ($dbConnection === null) {
198
      $dbConnection = DB::getInstance();
199
    }
200
201
    if ($_mysqli_client_version === null) {
202
      $_mysqli_client_version = \mysqli_get_client_version($dbConnection->getLink());
203
    }
204
205 1
    return $_mysqli_client_version;
206
  }
207 1
208
209
  /**
210
   * Returns a string representing the version of the MySQL server that the MySQLi extension is connected to.
211
   *
212 1
   * @param DB $dbConnection
213 1
   *
214 1
   * @return string
215 1
   */
216 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
    static $_mysqli_server_version = null;
219 1
220
    if ($dbConnection === null) {
221 1
      $dbConnection = DB::getInstance();
222 1
    }
223 1
224
    if ($_mysqli_server_version === null) {
225 1
      $_mysqli_server_version = \mysqli_get_server_version($dbConnection->getLink());
226
    }
227
228
    return $_mysqli_server_version;
229
  }
230
231
  /**
232 1
   * Return all db-fields from a table.
233
   *
234
   * @param string      $table
235
   * @param bool        $useStaticCache
236 1
   * @param DB|null     $dbConnection <p>use <strong>null</strong> if you will use the current database-connection</p>
237 1
   * @param null|string $databaseName <p>use <strong>null</strong> if you will use the current database</p>
238
   *
239 1
   * @return array
240 1
   */
241 1
  public static function getDbFields($table, $useStaticCache = true, DB $dbConnection = null, $databaseName = null)
242 1
  {
243 1
    static $DB_FIELDS_CACHE = array();
244
245
    // use the static cache
246 1
    if (
247
        $useStaticCache === true
248 1
        &&
249
        isset($DB_FIELDS_CACHE[$table])
250
    ) {
251
      return $DB_FIELDS_CACHE[$table];
252
    }
253
254
    // init
255
    $dbFields = array();
256
257
    if ($dbConnection === null) {
258
      $dbConnection = DB::getInstance();
259
    }
260
261
    if ($table === '') {
262
      $debug = new Debug($dbConnection);
263
      $debug->displayError('invalid table name');
264 1
265
      return array();
266
    }
267 1
268
    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 1
      $databaseName = $dbConnection->quote_string(trim($databaseName)) . '.';
270 1
    }
271 1
272
    $sql = 'SHOW COLUMNS FROM ' . $databaseName . $dbConnection->escape($table);
273 1
    $result = $dbConnection->query($sql);
274
275
    if ($result && $result->num_rows > 0) {
276
      foreach ($result->fetchAllArray() as $tmpResult) {
277
        $dbFields[] = $tmpResult['Field'];
278
      }
279
    }
280 1
281 1
    // add to static cache
282 1
    $DB_FIELDS_CACHE[$table] = $dbFields;
283 1
284
    return $dbFields;
285 1
  }
286
287
  /**
288
   * Copy row within a DB table and making updates to the columns.
289
   *
290 1
   * @param string  $table
291
   * @param array   $whereArray
292 1
   * @param array   $updateArray
293 1
   * @param array   $ignoreArray
294 1
   * @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 1
   * @return bool|int "int" (insert_id) by "<b>INSERT / REPLACE</b>"-queries<br />
298
   *                   "false" on error
299
   */
300
  public static function copyTableRow($table, array $whereArray, array $updateArray = array(), array $ignoreArray = array(), DB $dbConnection = null, $databaseName = null)
301 1
  {
302
    // init
303
    $table = trim($table);
304 1
305 1
    if ($dbConnection === null) {
306 1
      $dbConnection = DB::getInstance();
307
    }
308 1
309
    if ($table === '') {
310 1
      $debug = new Debug($dbConnection);
311 1
      $debug->displayError('invalid table name');
312 1
313 1
      return false;
314 1
    }
315 1
316 1
    $whereSQL = $dbConnection->_parseArrayPair($whereArray, 'AND');
317 1
    if ($whereSQL) {
318 1
      $whereSQL = 'AND ' . $whereSQL;
319
    }
320 1
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 1
    }
324 1
325
    // get the row
326
    $query = 'SELECT * FROM ' . $databaseName . $dbConnection->quote_string($table) . '
327 1
      WHERE 1 = 1
328 1
      ' . $whereSQL . '
329
    ';
330 1
    $result = $dbConnection->query($query);
331 1
332 1
    // make sure the row exists
333
    if ($result->num_rows > 0) {
334
335
      /** @noinspection LoopWhichDoesNotLoopInspection */
336
      /** @noinspection PhpAssignmentInConditionInspection */
337
      while ($tmpArray = $result->fetchArray()) {
338
339
        // re-build a new DB query and ignore some field-names
340
        $bindings = array();
341
        $insert_keys = '';
342
        $insert_values = '';
343
344
        foreach ($tmpArray as $fieldName => $value) {
345
346
          if (!in_array($fieldName, $ignoreArray, true)) {
347
            if (array_key_exists($fieldName, $updateArray)) {
348
              $insert_keys .= ',' . $fieldName;
349
              $insert_values .= ',?';
350
              $bindings[] = $updateArray[$fieldName]; // INFO: do not escape non selected data
351
            } else {
352
              $insert_keys .= ',' . $fieldName;
353
              $insert_values .= ',?';
354
              $bindings[] = $value; // INFO: do not escape non selected data
355
            }
356
          }
357
        }
358
359
        $insert_keys = ltrim($insert_keys, ',');
360
        $insert_values = ltrim($insert_values, ',');
361
362
        // insert the "copied" row
363
        $new_query = 'INSERT INTO ' . $databaseName . $dbConnection->quote_string($table) . ' 
364
          (' . $insert_keys . ')
365
          VALUES 
366
          (' . $insert_values . ')
367
        ';
368
        return $dbConnection->query($new_query, $bindings);
369
      }
370
    }
371
372
    return false;
373
  }
374
}
375