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 (#924)
by
unknown
04:35
created

MySqlDatabase::getInstance()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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