Passed
Push — master ( 3bf766...1a0132 )
by Joe
06:03
created

Generic::qr()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 3
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Generic SQL Driver Related Functionality
4
 * @author Joe Huss <[email protected]>
5
 * @copyright 2019
6
 * @package MyAdmin
7
 * @category SQL
8
 */
9
10
namespace MyDb;
11
12
/**
13
 * Class Generic
14
 */
15
abstract class Generic
16
{
17
	/* public: connection parameters */
18
	public $host = 'localhost';
19
	public $database = '';
20
	public $user = '';
21
	public $password = '';
22
23
	/* public: configuration parameters */
24
	public $autoStripslashes = false;
25
	public $Debug = 0; // Set to 1 for debugging messages.
26
	public $haltOnError = 'yes'; // "yes" (halt with message), "no" (ignore errors quietly), "report" (ignore error, but spit a warning)
27
28
	public $maxConnectErrors = 30;
29
	public $connectionAttempt = 0;
30
	public $maxMatches = 10000000;
31
32
	/**
33
	 * @var int
34
	 */
35
	public $autoFree = 0; // Set to 1 for automatic mysql_free_result()
36
37
	/* public: result array and current row number */
38
	public $Record = [];
39
	public $Row;
40
41
	/* public: current error number and error text */
42
	public $Errno = 0;
43
	public $Error = '';
44
45
	/* public: this is an api revision, not a CVS revision. */
46
	public $type = 'generic';
47
48
	/**
49
	 * @var int|object
50
	 */
51
	public $linkId = 0;
52
	public $queryId = 0;
53
54
	public $characterSet = 'utf8mb4';
55
	public $collation = 'utf8mb4_unicode_ci';
56
	
57
	/**
58
	 * Logged queries.
59
	 * @var array
60
	 */
61
	protected $log = [];    
62
63
	/**
64
	 * Constructs the db handler, can optionally specify connection parameters
65
	 *
66
	 * @param string $database Optional The database name
67
	 * @param string $user Optional The username to connect with
68
	 * @param string $password Optional The password to use
69
	 * @param string $host Optional The hostname where the server is, or default to localhost
70
	 * @param string $query Optional query to perform immediately
71
	 */
72 1
	public function __construct($database = '', $user = '', $password = '', $host = 'localhost', $query = '')
73
	{
74 1
		$this->database = $database;
75 1
		$this->user = $user;
76 1
		$this->password = $password;
77 1
		$this->host = $host;
78 1
		if ($query != '') {
79
			$this->query($query);
0 ignored issues
show
Bug introduced by
The method query() does not exist on MyDb\Generic. Did you maybe mean queryId()? ( Ignorable by Annotation )

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

79
			$this->/** @scrutinizer ignore-call */ 
80
          query($query);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
80
		}
81 1
		$this->connection_atttempt = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property connection_atttempt does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
82
	}
83
84
	/**
85
	 * @param string $message
86
	 * @param string $line
87
	 * @param string $file
88
	 * @return void
89
	 */
90 2
	public function log($message, $line = '', $file = '', $level = 'info')
0 ignored issues
show
Unused Code introduced by
The parameter $line is not used and could be removed. ( Ignorable by Annotation )

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

90
	public function log($message, /** @scrutinizer ignore-unused */ $line = '', $file = '', $level = 'info')

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $file is not used and could be removed. ( Ignorable by Annotation )

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

90
	public function log($message, $line = '', /** @scrutinizer ignore-unused */ $file = '', $level = 'info')

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $level is not used and could be removed. ( Ignorable by Annotation )

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

90
	public function log($message, $line = '', $file = '', /** @scrutinizer ignore-unused */ $level = 'info')

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
91
	{
92 2
		error_log($message);
93
	}
94
95
	/**
96
	 * @return int|object
97
	 */
98 2
	public function linkId()
99
	{
100 2
		return $this->linkId;
101
	}
102
103
	/**
104
	 * @return int|object
105
	 */
106 1
	public function queryId()
107
	{
108 1
		return $this->queryId;
109
	}
110
111
	/**
112
	 * @param $string
113
	 * @return string
114
	 */
115 1
	public function real_escape($string = '')
116
	{
117 1
		if ((!is_resource($this->linkId) || $this->linkId == 0) && !$this->connect()) {
0 ignored issues
show
introduced by
The condition is_resource($this->linkId) is always false.
Loading history...
Bug introduced by
The method connect() does not exist on MyDb\Generic. Since it exists in all sub-types, consider adding an abstract or default implementation to MyDb\Generic. ( Ignorable by Annotation )

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

117
		if ((!is_resource($this->linkId) || $this->linkId == 0) && !$this->/** @scrutinizer ignore-call */ connect()) {
Loading history...
118 1
			return $this->escape($string);
119
		}
120
		return mysqli_real_escape_string($this->linkId, $string);
0 ignored issues
show
Bug introduced by
It seems like $this->linkId can also be of type integer and resource; however, parameter $link of mysqli_real_escape_string() does only seem to accept mysqli, 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

120
		return mysqli_real_escape_string(/** @scrutinizer ignore-type */ $this->linkId, $string);
Loading history...
121
	}
122
123
	/**
124
	 * @param $string
125
	 * @return string
126
	 */
127 4
	public function escape($string = '')
128
	{
129
		//if (function_exists('mysql_escape_string'))
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
130
		//return mysql_escape_string($string);
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
131 4
		return str_replace(array('\\', "\0", "\n", "\r", "'", '"', "\x1a"), array('\\\\', '\\0', '\\n', '\\r', "\\'", '\\"', '\\Z'), $string);
132
	}
133
134
	/**
135
	 * @param mixed $str
136
	 * @return string
137
	 */
138 3
	public function dbAddslashes($str = '')
139
	{
140 3
		if (!isset($str) || $str == '') {
141 3
			return '';
142
		}
143 3
		return addslashes($str);
144
	}
145
146
	/**
147
	 * Db::toTimestamp()
148
	 * @param mixed $epoch
149
	 * @return bool|string
150
	 */
151 2
	public function toTimestamp($epoch)
152
	{
153 2
		return date('Y-m-d H:i:s', $epoch);
154
	}
155
156
	/**
157
	 * Db::fromTimestamp()
158
	 * @param mixed $timestamp
159
	 * @return bool|int|mixed
160
	 */
161 2
	public function fromTimestamp($timestamp)
162
	{
163 2
		if (preg_match('/([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})/', $timestamp, $parts)) {
164 2
			$time = mktime($parts[4], $parts[5], $parts[6], $parts[2], $parts[3], $parts[1]);
165
		} elseif (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})/', $timestamp, $parts)) {
166
			$time = mktime($parts[4], $parts[5], $parts[6], $parts[2], $parts[3], $parts[1]);
167
		} elseif (preg_match('/([0-9]{4})([0-9]{2})([0-9]{2})/', $timestamp, $parts)) {
168
			$time = mktime(1, 1, 1, $parts[2], $parts[3], $parts[1]);
169
		} elseif (is_numeric($timestamp) && $timestamp >= 943938000) {
170
			$time = $timestamp;
171
		} else {
172
			$this->log('Cannot Match Timestamp from '.$timestamp, __LINE__, __FILE__);
173
			$time = false;
174
		}
175 2
		return $time;
176
	}
177
178
	/**
179
	 * perform a query with limited result set
180
	 *
181
	 * @param string $queryString
182
	 * @param string|int $numRows
183
	 * @param int $offset
184
	 * @param string|int $line
185
	 * @param string $file
186
	 * @return mixed
187
	 */
188 1
	public function limitQuery($queryString, $numRows = '', $offset = 0, $line = '', $file = '')
189
	{
190 1
		if (!$numRows) {
191
			$numRows = $this->maxMatches;
192
		}
193 1
		if ($offset == 0) {
194 1
			$queryString .= ' LIMIT '.(int) $numRows;
195
		} else {
196 1
			$queryString .= ' LIMIT '.(int) $offset.','.(int) $numRows;
197
		}
198
199 1
		if ($this->Debug) {
200
			printf("Debug: limitQuery = %s<br>offset=%d, num_rows=%d<br>\n", $queryString, $offset, $numRows);
201
		}
202
203 1
		return $this->query($queryString, $line, $file);
204
	}
205
206
	/**
207
	 * db:qr()
208
	 *
209
	 *  alias of queryReturn()
210
	 *
211
	 * @param mixed $query SQL Query to be used
212
	 * @param string $line optionally pass __LINE__ calling the query for logging
213
	 * @param string $file optionally pass __FILE__ calling the query for logging
214
	 * @return mixed FALSE if no rows, if a single row it returns that, if multiple it returns an array of rows, associative responses only
215
	 */
216
	public function qr($query, $line = '', $file = '')
217
	{
218
		return $this->queryReturn($query, $line, $file);
0 ignored issues
show
Bug introduced by
The method queryReturn() does not exist on MyDb\Generic. It seems like you code against a sub-type of said class. However, the method does not exist in MyDb\Pdo\Db. Are you sure you never get one of those? ( Ignorable by Annotation )

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

218
		return $this->/** @scrutinizer ignore-call */ queryReturn($query, $line, $file);
Loading history...
219
	}
220
221
	/**
222
	 * gets a field
223
	 *
224
	 * @param mixed  $name
225
	 * @param string $stripSlashes
226
	 * @return string
227
	 */
228 2
	public function f($name, $stripSlashes = '')
229
	{
230 2
		if ($stripSlashes || ($this->autoStripslashes && !$stripSlashes)) {
231
			return stripslashes($this->Record[$name]);
232
		} else {
233 2
			return $this->Record[$name];
234
		}
235
	}
236
237
	/**
238
	 * error handling
239
	 *
240
	 * @param mixed $msg
241
	 * @param string $line
242
	 * @param string $file
243
	 * @return void
244
	 */
245 1
	public function halt($msg, $line = '', $file = '')
246
	{
247 1
		$this->unlock(false);
0 ignored issues
show
Bug introduced by
The method unlock() does not exist on MyDb\Generic. Since it exists in all sub-types, consider adding an abstract or default implementation to MyDb\Generic. ( Ignorable by Annotation )

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

247
		$this->/** @scrutinizer ignore-call */ 
248
         unlock(false);
Loading history...
248
		/* Just in case there is a table currently locked */
249
250
		//$this->Error = @$this->linkId->error;
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
251
		//$this->Errno = @$this->linkId->errno;
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
252 1
		if ($this->haltOnError == 'no') {
253
			return true;
254
		}
255 1
		if ($msg != '') {
256 1
			$this->haltmsg($msg, $line, $file);
257
		}
258 1
		if ($this->haltOnError != 'report') {
259 1
			echo '<p><b>Session halted.</b>';
260
			// FIXME! Add check for error levels
261 1
			if (isset($GLOBALS['tf'])) {
262
				$GLOBALS['tf']->terminate();
263
			}
264
		}
265 1
		return true;
266
	}
267
268
	/**
269
	 * @param mixed $msg
270
	 * @param string $line
271
	 * @param string $file
272
	 * @return mixed|void
273
	 */
274 2
	public function logBackTrace($msg, $line = '', $file = '')
0 ignored issues
show
Unused Code introduced by
The parameter $msg is not used and could be removed. ( Ignorable by Annotation )

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

274
	public function logBackTrace(/** @scrutinizer ignore-unused */ $msg, $line = '', $file = '')

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
275
	{
276 2
		$backtrace = (function_exists('debug_backtrace') ? debug_backtrace() : []);
277 2
		$this->log(
278 2
			('' !== getenv('REQUEST_URI') ? ' '.getenv('REQUEST_URI') : '').
279 2
			((isset($_POST) && count($_POST)) ? ' POST='.json_encode($_POST) : '').
280 2
			((isset($_GET) && count($_GET)) ? ' GET='.json_encode($_GET) : '').
281 2
			((isset($_FILES) && count($_FILES)) ? ' FILES='.json_encode($_FILES) : '').
282 2
			('' !== getenv('HTTP_USER_AGENT') ? ' AGENT="'.getenv('HTTP_USER_AGENT').'"' : '').
283 2
			(isset($_SERVER['REQUEST_METHOD']) ? ' METHOD="'.$_SERVER['REQUEST_METHOD'].'"'.
284 2
				($_SERVER['REQUEST_METHOD'] === 'POST' ? ' POST="'.json_encode($_POST).'"' : '') : ''),
285 2
			$line,
286 2
			$file,
287 2
			'error'
288
		);
289 2
		for ($level = 1, $levelMax = count($backtrace); $level < $levelMax; $level++) {
290 2
			$message = (isset($backtrace[$level]['file']) ? 'File: '.$backtrace[$level]['file'] : '').
291 2
				(isset($backtrace[$level]['line']) ? ' Line: '.$backtrace[$level]['line'] : '').
292 2
				' Function: '.(isset($backtrace[$level] ['class']) ? '(class '.$backtrace[$level] ['class'].') ' : '').
293 2
				(isset($backtrace[$level] ['type']) ? $backtrace[$level] ['type'].' ' : '').
294 2
				$backtrace[$level] ['function'].'(';
295 2
			if (isset($backtrace[$level] ['args'])) {
296 2
				for ($argument = 0, $argumentMax = count($backtrace[$level]['args']); $argument < $argumentMax; $argument++) {
297 2
					$message .= ($argument > 0 ? ', ' : '').
298 2
						(is_object($backtrace[$level]['args'][$argument]) ? 'class '.get_class($backtrace[$level]['args'][$argument]) : json_encode($backtrace[$level]['args'][$argument]));
299
				}
300
			}
301 2
			$message .= ')';
302 2
			$this->log($message, $line, $file, 'error');
303
		}
304
	}
305
306
	public function emailError($queryString, $error, $line, $file)
307
	{
308
		$subject = php_uname('n').' MySQLi Error';
309
		$smarty = new \TFSmarty();
0 ignored issues
show
Bug introduced by
The type TFSmarty was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
310
		$smarty->assign([
311
			'type' => $this->type,
312
			'queryString' => $queryString,
313
			'error' => $error,
314
			'line' => $line,
315
			'file' => $file,
316
			'request' => $_REQUEST,
317
			'server' => $_SERVER,
318
		]);
319
		if (isset($GLOBALS['tf'])) {
320
			$smarty->assign('account_id', $GLOBALS['tf']->session->account_id);
321
		}
322
		$email = $smarty->fetch('email/admin/sql_error.tpl');
323
		(new \MyAdmin\Mail())->adminMail($subject, $email, '[email protected]', 'admin/sql_error.tpl');
0 ignored issues
show
Bug introduced by
The type MyAdmin\Mail was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
324
		(new \MyAdmin\Mail())->adminMail($subject, $email, '[email protected]', 'admin/sql_error.tpl');
325
		$this->haltmsg('Invalid SQL: '.$queryString, $line, $file);
326
	}
327
	
328
	/**
329
	 * @param mixed $msg
330
	 * @param string $line
331
	 * @param string $file
332
	 * @return mixed|void
333
	 */
334 2
	public function haltmsg($msg, $line = '', $file = '')
335
	{
336 2
		$this->log("Database error: $msg", $line, $file, 'error');
337 2
		if ($this->Errno != '0' || !in_array($this->Error, ['', '()'])) {
338
			$sqlstate = mysqli_sqlstate($this->linkId);
0 ignored issues
show
Bug introduced by
It seems like $this->linkId can also be of type integer; however, parameter $link of mysqli_sqlstate() does only seem to accept mysqli, 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

338
			$sqlstate = mysqli_sqlstate(/** @scrutinizer ignore-type */ $this->linkId);
Loading history...
339
			$this->log("MySQLi SQLState: {$sqlstate}. Error: ".$this->Errno.' ('.$this->Error.')', $line, $file, 'error');
340
		}
341 2
		$this->logBackTrace($msg, $line, $file);
342
	}
343
344
	/**
345
	 * @return array
346
	 */
347 1
	public function indexNames()
348
	{
349 1
		return [];
350
	}
351
	
352
353
	/**
354
	 * Add query to logged queries.
355
	 * @param string $statement
356
	 * @param float $time Elapsed seconds with microseconds
357
	 * @param string|int $line Line Number
358
	 * @param string $file File Name
359
	 */
360 10
	public function addLog($statement, $time, $line = '', $file = '')
361
	{
362
		$query = [
363 10
			'statement' => $statement,
364 10
			'time' => $time * 1000
365
		];
366 10
		if ($line != '') {
367 5
			$query['line'] = $line;
368
		}
369 10
		if ($file != '') {
370 5
			$query['file'] = $file;
371
		}
372 10
		if (!isset($GLOBALS['db_queries'])) {
373 1
			$GLOBALS['db_queries'] = array();
374
		}
375 10
		$GLOBALS['db_queries'][] = $query;
376 10
		array_push($this->log, $query);
377
	}
378
379
	/**
380
	 * Return logged queries.
381
	 * @return array Logged queries
382
	 */
383
	public function getLog()
384
	{
385
		return $this->log;
386
	}    
387
}
388