Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Pull Request — master (#855)
by Dan
04:40
created

MySqlDatabase::requireRecord()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 2
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 3
rs 10
1
<?php declare(strict_types=1);
2
require_once(CONFIG . 'SmrMySqlSecrets.inc');
3
4
abstract class MySqlDatabase {
5
	// add configuration static members via traits
6
	use SmrMySqlSecrets;
7
8
	protected static $dbConn;
9
	protected static $selectedDbName;
10
	protected $dbResult = null;
11
	protected $dbRecord = null;
12
	
13
	public function __construct($dbName) {
14
		if (!self::$dbConn) {
15
			// Set the mysqli driver to raise exceptions on errors
16
			if (!mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)) {
17
				$this->error('Failed to enable mysqli error reporting');
18
			}
19
20
			self::$dbConn = new mysqli(self::$host, self::$user, self::$password,
21
			                           $dbName, self::$port, self::$socket);
22
			self::$selectedDbName = $dbName;
23
24
			// Default server charset should be set correctly. Using the default
25
			// avoids the additional query involved in `set_charset`.
26
			$charset = self::$dbConn->character_set_name();
27
			if ($charset != 'utf8') {
28
				$this->error('Unexpected charset: ' . $charset);
29
			}
30
		}
31
32
		// Do we need to switch databases (e.g. for compatability db access)?
33
		if (self::$selectedDbName != $dbName) {
34
			self::$dbConn->select_db($dbName);
35
			self::$selectedDbName = $dbName;
36
		}
37
	}
38
39
	/**
40
	 * Returns the size of the selected database in bytes.
41
	 */
42
	public function getDbBytes() {
43
		$query = 'SELECT SUM(data_length + index_length) as db_bytes FROM information_schema.tables WHERE table_schema=' . $this->escapeString(self::$selectedDbName);
44
		$result = self::$dbConn->query($query);
45
		return (int)$result->fetch_assoc()['db_bytes'];
46
	}
47
48
	// This should not be needed except perhaps by persistent connections
49
	public function close() {
50
		if (self::$dbConn) {
51
			self::$dbConn->close();
52
			self::$dbConn = false;
53
		}
54
	}
55
	
56
	public function query($query) {
57
		$this->dbResult = self::$dbConn->query($query);
58
	}
59
60
	/**
61
	 * Use to populate this instance with the next record of the active query.
62
	 */
63
	public function nextRecord() : bool {
64
		if (!$this->dbResult) {
65
			$this->error('No resource to get record from.');
66
		}
67
		
68
		if ($this->dbRecord = $this->dbResult->fetch_assoc()) {
69
			return true;
70
		}
71
		return false;
72
	}
73
74
	/**
75
	 * Use instead of nextRecord when exactly one record is expected from the
76
	 * active query.
77
	 */
78
	public function requireRecord() : void {
79
		if (!$this->nextRecord() || $this->getNumRows() != 1) {
80
			$this->error('One record required, but found ' . $this->getNumRows());
81
		}
82
	}
83
84
	public function hasField($name) {
85
		return isset($this->dbRecord[$name]);
86
	}
87
88
	public function getField($name) {
89
		return $this->dbRecord[$name];
90
	}
91
	
92
	public function getBoolean($name) {
93
		if ($this->dbRecord[$name] == 'TRUE') {
94
			return true;
95
		}
96
//		if($this->dbRecord[$name] == 'FALSE')
97
		return false;
98
//		throw new Exception('Field is not a boolean');
99
	}
100
	
101
	public function getInt($name) {
102
		return (int)$this->dbRecord[$name];
103
	}
104
	
105
	public function getFloat($name) {
106
		return (float)$this->dbRecord[$name];
107
	}
108
	
109
	// WARNING: In the past, Microtime was stored in the database incorrectly.
110
	// For backwards compatibility, set $pad_msec=true to try to guess at the
111
	// intended value. This is not safe if the Microtime length is wrong for an
112
	// unrelated reason!
113
	public function getMicrotime($name, $pad_msec = false) {
114
		$data = $this->dbRecord[$name];
115
		$sec  = substr($data, 0, 10);
116
		$msec = substr($data, 10);
117
		if (strlen($msec) != 6) {
118
			if ($pad_msec) {
119
				$msec = str_pad($msec, 6, '0', STR_PAD_LEFT);
120
			} else {
121
				throw new Exception('Field is not an escaped microtime (' . $data . ')');
122
			}
123
		}
124
		return "$sec.$msec";
125
	}
126
	
127
	public function getObject($name, $compressed = false) {
128
		$object = $this->getField($name);
129
		if ($compressed === true) {
130
			$object = gzuncompress($object);
131
		}
132
		return unserialize($object);
133
	}
134
	
135
	public function getRow() {
136
		return $this->dbRecord;
137
	}
138
	
139
	public function lockTable($table) {
140
		self::$dbConn->query('LOCK TABLES ' . $table . ' WRITE');
141
	}
142
	
143
	public function unlock() {
144
		self::$dbConn->query('UNLOCK TABLES');
145
	}
146
	
147
	public function getNumRows() {
148
		return $this->dbResult->num_rows;
149
	}
150
	
151
	public function getChangedRows() {
152
		return self::$dbConn->affected_rows;
153
	}
154
	
155
	public function getInsertID() {
156
		return self::$dbConn->insert_id;
157
	}
158
159
	protected function error($err) {
160
		throw new Exception($err);
161
	}
162
	
163
	public function escape($escape, $autoQuotes = true, $quotes = true) {
164
		if (is_bool($escape)) {
165
			if ($autoQuotes) {
166
				return $this->escapeBoolean($escape);
167
			} else {
168
				return $this->escapeBoolean($escape, $quotes);
169
			}
170
		}
171
		if (is_numeric($escape)) {
172
			return $this->escapeNumber($escape);
173
		}
174
		if (is_string($escape)) {
175
			if ($autoQuotes) {
176
				return $this->escapeString($escape);
177
			} else {
178
				return $this->escapeString($escape, $quotes);
179
			}
180
		}
181
		if (is_array($escape)) {
182
			return $this->escapeArray($escape, $autoQuotes, $quotes);
183
		}
184
		if (is_object($escape)) {
185
			if ($autoQuotes) {
186
				return $this->escapeObject($escape);
187
			} else {
188
				return $this->escapeObject($escape, $quotes);
189
			}
190
		}
191
	}
192
	
193
	public function escapeString($string, $quotes = true, $nullable = false) {
194
		if ($nullable === true && ($string === null || $string === '')) {
195
			return 'NULL';
196
		}
197
		if ($string === true) {
198
			$string = 'TRUE';
199
		} elseif ($string === false) {
200
			$string = 'FALSE';
201
		}
202
		if (is_array($string)) {
203
			$escapedString = '';
204
			foreach ($string as $value) {
205
				$escapedString .= $this->escapeString($value, $quotes) . ',';
206
			}
207
			return substr($escapedString, 0, -1);
208
		}
209
		if ($quotes) {
210
			return '\'' . self::$dbConn->real_escape_string($string) . '\'';
211
		}
212
		return self::$dbConn->real_escape_string($string);
213
	}
214
	
215
	public function escapeBinary($binary) {
216
		return '0x' . bin2hex($binary);
217
	}
218
	
219
	public function escapeArray(array $array, $autoQuotes = true, $quotes = true, $implodeString = ',', $escapeIndividually = true) {
220
		$string = '';
221
		if ($escapeIndividually) {
222
			foreach ($array as $value) {
223
				if (is_array($value)) {
224
					$string .= $this->escapeArray($value, $autoQuotes, $quotes, $implodeString, $escapeIndividually) . $implodeString;
225
				} else {
226
					$string .= $this->escape($value, $autoQuotes, $quotes) . $implodeString;
227
				}
228
			}
229
			$string = substr($string, 0, -1);
230
		} else {
231
			$string = $this->escape(implode($implodeString, $array), $autoQuotes, $quotes);
232
		}
233
		return $string;
234
	}
235
	
236
	public function escapeNumber($num) {
237
		// Numbers need not be quoted in MySQL queries, so if we know $num is
238
		// numeric, we can simply return its value (no quoting or escaping).
239
		if (is_numeric($num)) {
240
			return $num;
241
		} else {
242
			throw new Exception('Not a number! (' . $num . ')');
243
		}
244
	}
245
	
246
	public function escapeMicrotime($microtime, $quotes = false) {
247
		$sec_str = sprintf('%010d', $microtime);
248
		$usec_str = sprintf('%06d', fmod($microtime, 1) * 1E6);
249
		return $this->escapeString($sec_str . $usec_str, $quotes);
250
	}
251
	
252
	public function escapeBoolean($bool, $quotes = true) {
253
		if ($bool === true) {
254
			return $this->escapeString('TRUE', $quotes);
255
		} elseif ($bool === false) {
256
			return $this->escapeString('FALSE', $quotes);
257
		} else {
258
			throw new Exception('Not a boolean: ' . $bool);
259
		}
260
	}
261
	
262
	public function escapeObject($object, $compress = false, $quotes = true, $nullable = false) {
263
		if ($compress === true) {
264
			return $this->escapeBinary(gzcompress(serialize($object)));
265
		}
266
		return $this->escapeString(serialize($object), $quotes, $nullable);
267
	}
268
269
}
270