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