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 (#916)
by Dan
03:31
created

MySqlDatabase::switchDatabases()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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