Passed
Pull Request — development (#3445)
by Emanuele
06:45
created

Query::executeQuery()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 7
nc 2
nop 1
dl 0
loc 14
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file has all the main functions in it that relate to the mysql database.
5
 *
6
 * @package   ElkArte Forum
7
 * @copyright ElkArte Forum contributors
8
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file)
9
 *
10
 * This file contains code covered by:
11
 * copyright: 2011 Simple Machines (http://www.simplemachines.org)
12
 * copyright:    2004-2011, GreyWyvern - All rights reserved.
13
 *
14
 * @version 2.0 dev
15
 *
16
 */
17
18
namespace ElkArte\Database\Mysqli;
19
20
use ElkArte\Cache\Cache;
21
use ElkArte\Database\AbstractQuery;
22
use ElkArte\Errors\Errors;
23
use ElkArte\ValuesContainer;
24
25
/**
26
 * SQL database class, implements database class to control mysql functions
27
 */
28
class Query extends AbstractQuery
29
{
30
	/**
31
	 * {@inheritDoc}
32
	 */
33
	protected $ilike = ' LIKE ';
34
35
	/**
36
	 * {@inheritDoc}
37
	 */
38
	protected $not_ilike = ' NOT LIKE ';
39
40
	/**
41
	 * {@inheritDoc}
42 1
	 */
43
	protected $rlike = ' RLIKE ';
44 1
45
	/**
46 1
	 * {@inheritDoc}
47
	 */
48
	protected $not_rlike = ' NOT RLIKE ';
49 1
50
	/**
51
	 * {@inheritDoc}
52
	 */
53
	public function fix_prefix($db_prefix, $db_name)
54 1
	{
55
		return is_numeric(substr($db_prefix, 0, 1)) ? $db_name . '.' . $db_prefix : '`' . $db_name . '`.' . $db_prefix;
56 1
	}
57
58
	/**
59
	 * {@inheritDoc}
60
	 */
61
	public function transaction($type = 'commit')
62
	{
63
		if ($type === 'begin')
64
		{
65
			return @mysqli_query($this->connection, 'BEGIN');
0 ignored issues
show
Bug Best Practice introduced by
The expression return @mysqli_query($this->connection, 'BEGIN') also could return the type mysqli_result which is incompatible with the return type mandated by ElkArte\Database\QueryInterface::transaction() of boolean|resource.
Loading history...
Bug introduced by
$this->connection of type ElkArte\Database\ConnectionInterface is incompatible with the type mysqli expected by parameter $mysql of mysqli_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

65
			return @mysqli_query(/** @scrutinizer ignore-type */ $this->connection, 'BEGIN');
Loading history...
66
		}
67
68
		if ($type === 'rollback')
69
		{
70
			return @mysqli_query($this->connection, 'ROLLBACK');
0 ignored issues
show
Bug Best Practice introduced by
The expression return @mysqli_query($th...connection, 'ROLLBACK') also could return the type mysqli_result which is incompatible with the return type mandated by ElkArte\Database\QueryInterface::transaction() of boolean|resource.
Loading history...
71
		}
72
73
		if ($type === 'commit')
74
		{
75
			return @mysqli_query($this->connection, 'COMMIT');
0 ignored issues
show
Bug Best Practice introduced by
The expression return @mysqli_query($this->connection, 'COMMIT') also could return the type mysqli_result which is incompatible with the return type mandated by ElkArte\Database\QueryInterface::transaction() of boolean|resource.
Loading history...
76
		}
77
78
		return false;
79
	}
80 42
81
	/**
82
	 * {@inheritDoc}
83 42
	 */
84
	public function last_error()
85
	{
86
		if (is_object($this->connection))
87
		{
88
			return mysqli_error($this->connection);
0 ignored issues
show
Bug introduced by
$this->connection of type ElkArte\Database\ConnectionInterface is incompatible with the type mysqli expected by parameter $mysql of mysqli_error(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

88
			return mysqli_error(/** @scrutinizer ignore-type */ $this->connection);
Loading history...
89 42
		}
90
		else
91 30
		{
92
			return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the return type mandated by ElkArte\Database\QueryInterface::last_error() of string.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
93
		}
94
	}
95 42
96
	/**
97
	 * {@inheritDoc}
98 42
	 */
99 42
	public function insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false)
100
	{
101
		list($table, $indexed_columns, $insertRows) = $this->prepareInsert($table, $columns, $data);
102 42
103
		// Determine the method of insertion.
104 30
		$queryTitle = $method === 'replace' ? 'REPLACE' : ($method === 'ignore' ? 'INSERT IGNORE' : 'INSERT');
105
106
		// Do the insert.
107
		$this->result = $this->query('', '
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->query('', ' ' ...ity_override' => true)) can also be of type boolean. However, the property $result is declared as type ElkArte\Database\AbstractResult. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
108 40
			' . $queryTitle . ' INTO ' . $table . '(`' . implode('`, `', $indexed_columns) . '`)
109
			VALUES
110
				' . implode(',
111 42
				', $insertRows),
112
			array(
113
				'security_override' => true,
114 42
			)
115
		);
116
117 42
		$this->result->updateDetails([
118 42
			'connection' => $this->connection
119
		]);
120 42
121
		return $this->result;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->result also could return the type ElkArte\Database\AbstractResult which is incompatible with the return type mandated by ElkArte\Database\QueryInterface::insert() of boolean|resource.
Loading history...
122
	}
123
124 42
	/**
125
	 * {@inheritDoc}
126 42
	 */
127 42
	public function replace($table, $columns, $data, $keys, $disable_trans = false)
128
	{
129 42
		return $this->insert('replace', $table, $columns, $data, $keys, $disable_trans);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->insert('re... $keys, $disable_trans) also could return the type ElkArte\Database\AbstractResult which is incompatible with the return type mandated by ElkArte\Database\QueryInterface::replace() of boolean|resource.
Loading history...
130 42
	}
131
132 42
	/**
133 37
	 * {@inheritDoc}
134
	 */
135 42
	protected function initialChecks($db_string, $db_values, $identifier = '')
136
	{
137
		// Use "ORDER BY null" to prevent Mysql doing filesorts for Group By clauses without an Order By
138
		if (strpos($db_string, 'GROUP BY') !== false
139 42
			&& strpos($db_string, 'ORDER BY') === false
140 42
			&& strpos($db_string, 'INSERT INTO') === false)
141 42
		{
142 42
			if (($pos = strpos($db_string, 'LIMIT ')) !== false)
143
			{
144
				// Add before LIMIT
145
				$db_string = substr($db_string, 0, $pos) . "\t\t\tORDER BY null\n" . substr($db_string, $pos, strlen($db_string));
146 42
			}
147
			else
148
			{
149
				// Append it.
150
				$db_string .= "\n\t\t\tORDER BY null";
151
			}
152 153
		}
153
		return $db_string;
154
	}
155 153
156
	protected function executeQuery($db_string)
157
	{
158 153
		if (!$this->_unbuffered)
159 153
		{
160 153
			$this->_db_last_result = @mysqli_query($this->connection, $db_string);
0 ignored issues
show
Bug introduced by
$this->connection of type ElkArte\Database\ConnectionInterface is incompatible with the type mysqli expected by parameter $mysql of mysqli_query(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

160
			$this->_db_last_result = @mysqli_query(/** @scrutinizer ignore-type */ $this->connection, $db_string);
Loading history...
Documentation Bug introduced by
It seems like @mysqli_query($this->connection, $db_string) of type boolean or mysqli_result is incompatible with the declared type resource of property $_db_last_result.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
161
		}
162 14
		else
163
		{
164
			$this->_db_last_result = @mysqli_query($this->connection, $db_string, MYSQLI_USE_RESULT);
165 2
		}
166
167
		$this->result = new Result($this->_db_last_result,
168
			new ValuesContainer([
169
				'connection' => $this->connection
170 12
			])
171
		);
172
	}
173
174 153
	/**
175
	 * {@inheritDoc}
176
	 */
177 153
	public function error($db_string)
178
	{
179 153
		global $txt, $webmaster_email, $modSettings, $db_persist;
180
		global $db_server, $db_user, $db_passwd, $db_name, $ssi_db_user, $ssi_db_passwd;
181 153
182
		// We'll try recovering the file and line number the original db query was called from.
183 153
		list ($file, $line) = $this->backtrace_message();
184
185
		// Just in case nothing can be found from debug_backtrace
186
		$file = $file ?? __FILE__;
187
		$line = $line ?? __LINE__;
188
189
		// This is the error message...
190 153
		$query_error = mysqli_error($this->connection);
0 ignored issues
show
Bug introduced by
$this->connection of type ElkArte\Database\ConnectionInterface is incompatible with the type mysqli expected by parameter $mysql of mysqli_error(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

190
		$query_error = mysqli_error(/** @scrutinizer ignore-type */ $this->connection);
Loading history...
191
		$query_errno = mysqli_errno($this->connection);
0 ignored issues
show
Bug introduced by
$this->connection of type ElkArte\Database\ConnectionInterface is incompatible with the type mysqli expected by parameter $mysql of mysqli_errno(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

191
		$query_errno = mysqli_errno(/** @scrutinizer ignore-type */ $this->connection);
Loading history...
192
193
		// Error numbers:
194
		//    1016: Can't open file '....MYI'
195
		//    1030: Got error ??? from table handler.
196 153
		//    1034: Incorrect key file for table.
197
		//    1035: Old key file for table.
198 4
		//    1142: Command denied
199
		//    1205: Lock wait timeout exceeded.
200
		//    1213: Deadlock found.
201
		//    2006: Server has gone away.
202 153
		//    2013: Lost connection to server during query.
203
204 153
		// We cannot do something, try to find out what and act accordingly
205 153
		if ($query_errno == 1142)
206 153
		{
207
			$command = substr(trim($db_string), 0, 6);
208
			if ($command === 'DELETE' || $command === 'UPDATE' || $command === 'INSERT')
209
			{
210 153
				// We can try to ignore it (warning the admin though it's a thing to do)
211
				// and serve the page just SELECTing
212
				$_SESSION['query_command_denied'][$command] = $query_error;
213
214
				// Let the admin know there is a command denied issue
215
				Errors::instance()->log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n$db_string" : ''), 'database', $file, $line);
216
217
				return false;
218
			}
219
		}
220
221
		// Database error auto fixing ;).
222
		if (function_exists('\\ElkArte\\Cache\\Cache::instance()->get') && (!isset($modSettings['autoFixDatabase']) || $modSettings['autoFixDatabase'] == '1'))
223
		{
224
			$db_last_error = db_last_error();
225
			$cache = Cache::instance();
226
227
			// Force caching on, just for the error checking.
228
			$old_cache = $cache->getLevel();
229
			if ($cache->isEnabled() === false)
230
			{
231
				$cache->setLevel(1);
232
			}
233
			$temp = null;
234
235
			if ($cache->getVar($temp, 'db_last_error', 600))
236
			{
237
				$db_last_error = max($db_last_error, $temp);
238
			}
239
240
			if ($db_last_error < time() - 3600 * 24 * 3)
241
			{
242
				// We know there's a problem... but what?  Try to auto detect.
243
				if ($query_errno == 1030 && strpos($query_error, ' 127 ') !== false)
244
				{
245
					preg_match_all('~(?:[\n\r]|^)[^\']+?(?:FROM|JOIN|UPDATE|TABLE) ((?:[^\n\r(]+?(?:, )?)*)~s', $db_string, $matches);
246
247
					$fix_tables = array();
248
					foreach ($matches[1] as $tables)
249
					{
250
						$tables = array_unique(explode(',', $tables));
251
						foreach ($tables as $table)
252
						{
253
							// Now, it's still theoretically possible this could be an injection.  So backtick it!
254
							if (trim($table) !== '')
255
							{
256
								$fix_tables[] = '`' . strtr(trim($table), array('`' => '')) . '`';
257
							}
258
						}
259
					}
260
261
					$fix_tables = array_unique($fix_tables);
262
				}
263
				// Table crashed.  Let's try to fix it.
264
				elseif ($query_errno == 1016)
265
				{
266
					if (preg_match('~\'([^\.\']+)~', $query_error, $match) != 0)
267
					{
268
						$fix_tables = array('`' . $match[1] . '`');
269
					}
270
				}
271
				// Indexes crashed.  Should be easy to fix!
272
				elseif ($query_errno == 1034 || $query_errno == 1035)
273
				{
274
					preg_match('~\'([^\']+?)\'~', $query_error, $match);
275
					$fix_tables = array('`' . $match[1] . '`');
276
				}
277
			}
278
279
			// Check for errors like 145... only fix it once every three days, and send an email. (can't use empty because it might not be set yet...)
280
			if (!empty($fix_tables))
281
			{
282
				// sources/Logging.php for logLastDatabaseError(), subs/Mail.subs.php for sendmail().
283
				// @todo this should go somewhere else, not into the db-mysql layer I think
284
				require_once(SOURCEDIR . '/Logging.php');
285
				require_once(SUBSDIR . '/Mail.subs.php');
286
287
				// Make a note of the REPAIR...
288
				$cache->put('db_last_error', time(), 600);
289
				if (!$cache->getVar($temp, 'db_last_error', 600))
290
				{
291
					logLastDatabaseError();
292
				}
293
294
				// Attempt to find and repair the broken table.
295
				foreach ($fix_tables as $table)
296
				{
297
					$this->query('', "
298
						REPAIR TABLE $table", false);
299
				}
300
301
				// And send off an email!
302
				sendmail($webmaster_email, $txt['database_error'], $txt['tried_to_repair']);
303
304
				$modSettings['cache_enable'] = $old_cache;
305
306
				// Try the query again...?
307
				$ret = $this->query('', $db_string, false);
308
				if ($ret->hasResults())
309
				{
310
					return $ret;
311
				}
312
			}
313
			else
314
			{
315
				$modSettings['cache_enable'] = $old_cache;
316
			}
317
318
			// Check for the "lost connection" or "deadlock found" errors - and try it just one more time.
319
			if (in_array($query_errno, array(1205, 1213, 2006, 2013)))
320
			{
321
				$new_connection = false;
322
				if (in_array($query_errno, array(2006, 2013)))
323
				{
324
					// Are we in SSI mode?  If so try that username and password first
325
					if (ELK == 'SSI' && !empty($ssi_db_user) && !empty($ssi_db_passwd))
326
					{
327
						$new_connection = @mysqli_connect((!empty($db_persist) ? 'p:' : '') . $db_server, $ssi_db_user, $ssi_db_passwd, $db_name);
0 ignored issues
show
Bug introduced by
The call to mysqli_connect() has too few arguments starting with port. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

327
						$new_connection = @/** @scrutinizer ignore-call */ mysqli_connect((!empty($db_persist) ? 'p:' : '') . $db_server, $ssi_db_user, $ssi_db_passwd, $db_name);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
328
					}
329
330
					// Fall back to the regular username and password if need be
331
					if (!$new_connection)
332
					{
333
						$new_connection = @mysqli_connect((!empty($db_persist) ? 'p:' : '') . $db_server, $db_user, $db_passwd, $db_name);
334
					}
335
				}
336
337
				if ($new_connection)
338
				{
339
					$this->connection = $new_connection;
0 ignored issues
show
Documentation Bug introduced by
It seems like $new_connection can also be of type mysqli. However, the property $connection is declared as type ElkArte\Database\ConnectionInterface. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
340
341
					// Try a deadlock more than once more.
342
					for ($n = 0; $n < 4; $n++)
343
					{
344
						$ret = $this->query('', $db_string, false);
345
346
						$new_errno = mysqli_errno($new_connection);
347
						if ($ret->hasResults() || in_array($new_errno, array(1205, 1213)))
348
						{
349
							break;
350
						}
351
					}
352
353
					// If it failed again, shucks to be you... we're not trying it over and over.
354
					if ($ret->hasResults())
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $ret does not seem to be defined for all execution paths leading up to this point.
Loading history...
355
					{
356
						return $ret;
357
					}
358
				}
359
			}
360
			// Are they out of space, perhaps?
361
			elseif ($query_errno == 1030 && (strpos($query_error, ' -1 ') !== false || strpos($query_error, ' 28 ') !== false || strpos($query_error, ' 12 ') !== false))
362
			{
363
				if (!isset($txt))
364
				{
365
					$query_error .= ' - check database storage space.';
366
				}
367
				else
368
				{
369
					if (!isset($txt['mysql_error_space']))
370
					{
371
						theme()->getTemplates()->loadLanguageFile('Errors');
372
					}
373
374
					$query_error .= !isset($txt['mysql_error_space']) ? ' - check database storage space.' : $txt['mysql_error_space'];
375
				}
376
			}
377
		}
378
379
		// Log the error.
380
		if ($query_errno != 1213 && $query_errno != 1205)
381
		{
382
			Errors::instance()->log_error($txt['database_error'] . ': ' . $query_error . (!empty($modSettings['enableErrorQueryLogging']) ? "\n\n$db_string" : ''), 'database', $file, $line);
383
		}
384
385
		$this->throwError($db_string, $query_error, $file, $line);
386
	}
387
388
	/**
389
	 * Unescape an escaped string!
390
	 *
391
	 * @param string $string
392
	 *
393
	 * @return string
394
	 */
395
	public function unescape_string($string)
396
	{
397
		return stripslashes($string);
398
	}
399
400
	/**
401
	 * {@inheritDoc}
402
	 */
403
	public function support_ignore()
404
	{
405
		return true;
0 ignored issues
show
Bug Best Practice introduced by
The expression return true returns the type true which is incompatible with the return type mandated by ElkArte\Database\AbstractQuery::support_ignore() of false.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
406
	}
407
408
	/**
409
	 * {@inheritDoc}
410
	 */
411
	public function server_version()
412
	{
413
		$request = $this->query('', '
414
			SELECT VERSION()',
415
			array()
416
		);
417
		list ($ver) = $request->fetch_row();
418
		$request->free_result();
419
420
		return $ver;
421
	}
422
423
	/**
424
	 * {@inheritDoc}
425
	 */
426
	public function title()
427
	{
428
		return 'MySQL';
429
	}
430
431
	/**
432
	 * {@inheritDoc}
433
	 */
434
	public function case_sensitive()
435
	{
436
		return false;
437
	}
438
439
	/**
440
	 * {@inheritDoc}
441
	 */
442
	public function escape_string($string)
443
	{
444
		$string = $this->_clean_4byte_chars($string);
445
446
		return mysqli_real_escape_string($this->connection, $string);
0 ignored issues
show
Bug introduced by
$this->connection of type ElkArte\Database\ConnectionInterface is incompatible with the type mysqli expected by parameter $mysql of mysqli_real_escape_string(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

446
		return mysqli_real_escape_string(/** @scrutinizer ignore-type */ $this->connection, $string);
Loading history...
447
	}
448
449
	/**
450
	 * Checks if the string contains any 4byte chars and if so,
451
	 * converts them into HTML entities.
452
	 *
453
	 * This is necessary because MySQL utf8 doesn't know how to store such
454
	 * characters and would generate an error any time one is used.
455
	 * The 4byte chars are used by emoji
456
	 *
457
	 * @param string $string
458
	 * @return string
459
	 */
460
	protected function _clean_4byte_chars($string)
461
	{
462
		global $modSettings;
463
464
		if (!empty($modSettings['using_utf8mb4']))
465
		{
466 1
			return $string;
467
		}
468 1
469
		$result = $string;
470
		$ord = array_map('ord', str_split($string));
0 ignored issues
show
Bug introduced by
It seems like str_split($string) can also be of type true; however, parameter $array of array_map() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

470
		$ord = array_map('ord', /** @scrutinizer ignore-type */ str_split($string));
Loading history...
471
472
		// If we are in the 4-byte range
473
		if (max($ord) >= 240)
474
		{
475
			// Byte length
476
			$length = strlen($string);
477
			$result = '';
478
479
			// Look for a 4byte marker
480
			for ($i = 0; $i < $length; $i++)
481
			{
482
				// The first byte of a 4-byte character encoding starts with the bytes 0xF0-0xF4 (240 <-> 244)
483
				// but look all the way to 247 for safe measure
484
				$ord1 = $ord[$i];
485
				if ($ord1 >= 240 && $ord1 <= 247)
486
				{
487
					// Replace it with the corresponding html entity
488
					$entity = $this->_uniord(chr($ord[$i]) . chr($ord[$i + 1]) . chr($ord[$i + 2]) . chr($ord[$i + 3]));
489 16
					if ($entity === false)
490
					{
491 16
						$result .= "\xEF\xBF\xBD";
492
					}
493
					else
494
					{
495
						$result .= '&#x' . dechex($entity) . ';';
496
					}
497 1
					$i += 3;
498
				}
499 1
				else
500
				{
501
					$result .= $string[$i];
502
				}
503
			}
504
		}
505 62
506
		return $result;
507 62
	}
508
509 62
	/**
510
	 * Converts a 4byte char into the corresponding HTML entity code.
511
	 *
512
	 * This function is derived from:
513
	 * http://www.greywyvern.com/code/php/utf8_html.phps
514
	 *
515
	 * @param string $c
516
	 * @return int|false
517
	 */
518
	protected function _uniord($c)
519
	{
520
		if (ord($c[0]) >= 0 && ord($c[0]) <= 127)
521
		{
522
			return ord($c[0]);
523 62
		}
524
		if (ord($c[0]) >= 192 && ord($c[0]) <= 223)
525 62
		{
526
			return (ord($c[0]) - 192) * 64 + (ord($c[1]) - 128);
527 62
		}
528
		if (ord($c[0]) >= 224 && ord($c[0]) <= 239)
529
		{
530
			return (ord($c[0]) - 224) * 4096 + (ord($c[1]) - 128) * 64 + (ord($c[2]) - 128);
531
		}
532 62
		if (ord($c[0]) >= 240 && ord($c[0]) <= 247)
533 62
		{
534
			return (ord($c[0]) - 240) * 262144 + (ord($c[1]) - 128) * 4096 + (ord($c[2]) - 128) * 64 + (ord($c[3]) - 128);
535
		}
536 62
		if (ord($c[0]) >= 248 && ord($c[0]) <= 251)
537
		{
538
			return (ord($c[0]) - 248) * 16777216 + (ord($c[1]) - 128) * 262144 + (ord($c[2]) - 128) * 4096 + (ord($c[3]) - 128) * 64 + (ord($c[4]) - 128);
539
		}
540
		if (ord($c[0]) >= 252 && ord($c[0]) <= 253)
541
		{
542
			return (ord($c[0]) - 252) * 1073741824 + (ord($c[1]) - 128) * 16777216 + (ord($c[2]) - 128) * 262144 + (ord($c[3]) - 128) * 4096 + (ord($c[4]) - 128) * 64 + (ord($c[5]) - 128);
543
		}
544
		if (ord($c[0]) >= 254 && ord($c[0]) <= 255)
545
		{
546
			return false;
547
		}
548
	}
549
550
	/**
551
	 * {@inheritDoc}
552
	 */
553
	public function server_info()
554
	{
555
		return mysqli_get_server_info($this->connection);
0 ignored issues
show
Bug introduced by
$this->connection of type ElkArte\Database\ConnectionInterface is incompatible with the type mysqli expected by parameter $mysql of mysqli_get_server_info(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

555
		return mysqli_get_server_info(/** @scrutinizer ignore-type */ $this->connection);
Loading history...
556
	}
557
558
	/**
559
	 * {@inheritDoc}
560
	 */
561
	public function client_version()
562
	{
563
		$request = $this->query('', '
564
			SELECT VERSION()',
565
			array()
566
		);
567
		list ($ver) = $request->fetch_row();
568
		$request->free_result();
569 62
570
		return $ver;
571
	}
572
573
	/**
574
	 * {@inheritdoc}
575
	 */
576
	public function select_db($dbName = null)
577
	{
578
		return mysqli_select_db($this->connection, $dbName);
0 ignored issues
show
Bug introduced by
$this->connection of type ElkArte\Database\ConnectionInterface is incompatible with the type mysqli expected by parameter $mysql of mysqli_select_db(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

578
		return mysqli_select_db(/** @scrutinizer ignore-type */ $this->connection, $dbName);
Loading history...
579
	}
580
581
	/**
582
	 * {@inheritdoc}
583
	 */
584
	public function validConnection()
585
	{
586
		return is_object($this->connection);
587
	}
588
589
	/**
590
	 * {@inheritDoc}
591
	 */
592
	public function list_tables($db_name_str = false, $filter = false)
593
	{
594
		$dump = new Dump($this);
595
596
		return $dump->list_tables($db_name_str, $filter);
597
	}
598
599
	/**
600
	 * {@inheritDoc}
601
	 */
602
	public function supportMediumtext()
603
	{
604
		return true;
605
	}
606
607
	/**
608
	 * {@inheritdoc}
609
	 */
610
	protected function _replaceStringCaseSensitive($replacement)
611
	{
612
		return 'BINARY ' . $this->_replaceString($replacement);
613
	}
614
615
	/**
616
	 * {@inheritdoc}
617
	 */
618
	protected function _replaceStringCaseInsensitive($replacement)
619
	{
620
		return $this->_replaceString($replacement);
621
	}
622
623
	/**
624
	 * Casts the column to LOWER(column_name) for replacement__callback.
625
	 *
626
	 * @param mixed $replacement
627
	 * @return string
628
	 */
629
	protected function _replaceColumnCaseInsensitive($replacement)
630
	{
631
		return $replacement;
632
	}
633
}
634