Completed
Push — master ( 81b097...fd0ca9 )
by Nazar
19:45 queued 06:27
created

MySQLi   C

Complexity

Total Complexity 69

Size/Duplication

Total Lines 320
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 1

Test Coverage

Coverage 70.92%

Importance

Changes 0
Metric Value
dl 0
loc 320
ccs 139
cts 196
cp 0.7092
rs 5.6445
c 0
b 0
f 0
wmc 69
lcom 3
cbo 1

18 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 28 4
B get_host_and_port() 0 20 6
B q_internal() 0 17 5
B q_internal_once() 0 19 6
A q_internal_once_bind_param() 0 15 2
A f() 0 9 3
B f_result() 0 16 9
B f_stmt() 0 21 6
A f_stmt_bind_result() 0 12 2
B f_stmt_internal() 0 18 6
A id() 0 3 1
A affected() 0 3 1
A free() 0 9 3
B columns() 0 12 5
A tables() 0 8 4
A s_internal() 0 4 2
A server() 0 3 1
A __destruct() 0 6 3

How to fix   Complexity   

Complex Class

Complex classes like MySQLi often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MySQLi, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2011-2017, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs\DB;
9
use
10
	mysqli_result,
11
	mysqli_stmt;
12
13
class MySQLi extends _Abstract {
14
	/**
15
	 * @var \MySQLi Instance of DB connection
16
	 */
17
	protected $instance;
18
	/**
19
	 * @inheritdoc
20
	 */
21 32
	public function __construct ($database, $user = '', $password = '', $host = 'localhost', $prefix = '') {
22 32
		$start = microtime(true);
23
		/**
24
		 * Parsing of $host variable, detecting port and persistent connection
25
		 */
26 32
		list($host, $port) = $this->get_host_and_port($host);
27 32
		$this->instance = new \MySQLi($host, $user, $password, $database, $port);
28 32
		if (!is_object($this->instance) || $this->instance->connect_errno) {
29 1
			return;
30
		}
31 32
		$this->database = $database;
32
		/**
33
		 * Changing DB charset if necessary
34
		 */
35 32
		if ($this->instance->character_set_name() != 'utf8mb4') {
36 32
			$this->instance->set_charset('utf8mb4');
37
		}
38
		/**
39
		 * Force strict mode
40
		 */
41 32
		$this->instance->query(
42 32
			"SET SESSION sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'"
43
		);
44 32
		$this->connected       = true;
45 32
		$this->connecting_time = microtime(true) - $start;
46 32
		$this->db_type         = 'mysql';
47 32
		$this->prefix          = $prefix;
48 32
	}
49
	/**
50
	 * Parse host string into host and port separately
51
	 *
52
	 * Understands `p:` prefix for persistent connections
53
	 *
54
	 * @param string $host_string
55
	 *
56
	 * @return array
57
	 */
58 32
	protected function get_host_and_port ($host_string) {
59 32
		$host = explode(':', $host_string);
60 32
		$port = ini_get('mysqli.default_port') ?: 3306;
61 32
		switch (count($host)) {
62 32
			case 1:
63 32
				$host = $host[0];
64 32
				break;
65 1
			case 2:
66 1
				if ($host[0] == 'p') {
67 1
					$host = "$host[0]:$host[1]";
68
				} else {
69 1
					list($host, $port) = $host;
70
				}
71 1
				break;
72 1
			case 3:
73 1
				$port = $host[2];
74 1
				$host = "$host[0]:$host[1]";
75
		}
76 32
		return [$host, $port];
77
	}
78
	/**
79
	 * @inheritdoc
80
	 *
81
	 * @return bool|mysqli_result|mysqli_stmt
82
	 */
83 32
	protected function q_internal ($query, $parameters = []) {
84 32
		if (!$query) {
85 1
			return false;
86
		}
87 32
		$stmt = $this->q_internal_once($query, $parameters);
88
		/*
89
		 * In case of MySQL Client error try once again
90
		 */
91
		if (
92 32
			!$stmt &&
93 32
			$this->instance->errno >= 2000 &&
94 32
			$this->instance->ping()
95
		) {
96
			$stmt = $this->q_internal_once($query, $parameters);
97
		}
98 32
		return $stmt;
99
	}
100
	/**
101
	 * @param string $query
102
	 * @param array  $parameters
103
	 *
104
	 * @return bool|mysqli_result|mysqli_stmt
105
	 */
106 32
	protected function q_internal_once ($query, $parameters) {
107 32
		if (!$parameters) {
108 32
			return $this->instance->query($query);
109
		}
110 27
		$stmt = $this->instance->prepare($query);
111 27
		if (!$stmt) {
112
			return false;
113
		}
114
		// Allows to provide more parameters for prepared statements than needed
115 27
		$local_parameters = array_slice($parameters, 0, substr_count($query, '?'));
116 27
		if (!$this->q_internal_once_bind_param($stmt, $local_parameters)) {
117
			return false;
118
		}
119 27
		$result = $stmt->execute();
120
		/**
121
		 * Return result only for SELECT queries, boolean otherwise
122
		 */
123 27
		return $result && strpos($query, 'SELECT') === 0 ? $stmt : $result;
124
	}
125
	/**
126
	 * @param mysqli_stmt $stmt
127
	 * @param array       $local_parameters
128
	 *
129
	 * @return bool
130
	 */
131 27
	protected function q_internal_once_bind_param ($stmt, $local_parameters) {
132
		/**
133
		 * Hack: Until https://github.com/facebook/hhvm/issues/6229 is fixed
134
		 *
135
		 * Some black magic to please HHVM, ideally only last line is necessary
136
		 */
137 27
		$tmp = [];
138 27
		foreach (array_keys($local_parameters) as $key) {
139 27
			$tmp[$key] = &$local_parameters[$key];
140
		}
141 27
		return $stmt->bind_param(
142 27
			str_repeat('s', count($local_parameters)),
143 27
			...$local_parameters
144
		);
145
	}
146
	/**
147
	 * @inheritdoc
148
	 *
149
	 * @param false|mysqli_result|mysqli_stmt $query_result_stmt
150
	 */
151 32
	public function f ($query_result_stmt, $single_column = false, $array = false, $indexed = false) {
152 32
		if ($query_result_stmt instanceof mysqli_result) {
153 30
			return $this->f_result($query_result_stmt, $single_column, $array, $indexed);
154
		}
155 25
		if ($query_result_stmt instanceof mysqli_stmt) {
156 25
			return $this->f_stmt($query_result_stmt, $single_column, $array, $indexed);
157
		}
158 1
		return false;
159
	}
160
	/**
161
	 * @param mysqli_result $query_result
162
	 * @param bool          $single_column
163
	 * @param bool          $array
164
	 * @param bool          $indexed
165
	 *
166
	 * @return array|bool|mixed
167
	 */
168 30
	protected function f_result ($query_result, $single_column, $array, $indexed) {
169 30
		$result_type = $single_column || $indexed ? MYSQLI_NUM : MYSQLI_ASSOC;
170 30
		if ($array) {
171 21
			$result = [];
172 21
			while ($current = $query_result->fetch_array($result_type)) {
173 20
				$result[] = $single_column ? $current[0] : $current;
174
			}
175 21
			$this->free($query_result);
176 21
			return $result;
177
		}
178 26
		$result = $query_result->fetch_array($result_type);
179 26
		if ($result === null) {
180 22
			$result = false;
181
		}
182 26
		return $single_column && $result ? $result[0] : $result;
183
	}
184
	/**
185
	 * @param mysqli_stmt $stmt
186
	 * @param bool        $single_column
187
	 * @param bool        $array
188
	 * @param bool        $indexed
189
	 *
190
	 * @return array|bool|mixed
191
	 */
192 25
	protected function f_stmt ($stmt, $single_column, $array, $indexed) {
193 25
		$meta    = $stmt->result_metadata();
194 25
		$result  = [];
195 25
		$columns = [];
196 25
		while ($field = $meta->fetch_field()) {
197 25
			$result[]  = null;
198 25
			$columns[] = $field->name;
199
		}
200 25
		if (!$stmt->store_result() || !$this->f_stmt_bind_result($stmt, $result)) {
201
			return false;
202
		}
203 25
		if ($array) {
204 9
			$return_result = [];
205 9
			while ($current_result = $this->f_stmt_internal($stmt, $result, $single_column, $indexed, $columns)) {
206 9
				$return_result[] = $current_result;
207
			}
208 9
			$this->free($stmt);
209 9
			return $return_result;
210
		}
211 25
		return $this->f_stmt_internal($stmt, $result, $single_column, $indexed, $columns);
212
	}
213
	/**
214
	 * @param mysqli_stmt $stmt
215
	 * @param array       $result
216
	 *
217
	 * @return bool
218
	 */
219 25
	protected function f_stmt_bind_result ($stmt, &$result) {
220
		/**
221
		 * Hack: Until https://github.com/facebook/hhvm/issues/6229 is fixed
222
		 *
223
		 * Some black magic to please HHVM, ideally only last line is necessary
224
		 */
225 25
		$tmp = [];
226 25
		foreach (array_keys($result) as $key) {
227 25
			$tmp[$key] = &$result[$key];
228
		}
229 25
		return $stmt->bind_result(...$result);
230
	}
231
	/**
232
	 * @param mysqli_stmt $stmt
233
	 * @param array       $result
234
	 * @param bool        $single_column
235
	 * @param bool        $indexed
236
	 * @param string[]    $columns
237
	 *
238
	 * @return array|bool
239
	 */
240 25
	protected function f_stmt_internal ($stmt, $result, $single_column, $indexed, $columns) {
241 25
		if (!$stmt->fetch()) {
242 10
			return false;
243
		}
244 25
		if ($single_column && $result) {
245 8
			return $result[0];
246
		}
247
		// Hack: `$result`'s values are all references, we need to dereference them into plain values
248 24
		$new_result = [];
249 24
		foreach ($result as $i => $v) {
250 24
			$new_result[$i] = $v;
251
		}
252 24
		$result = $new_result;
253 24
		if ($indexed) {
254 1
			return $result;
255
		}
256 24
		return array_combine($columns, $result);
257
	}
258
	/**
259
	 * @inheritdoc
260
	 */
261 24
	public function id () {
262 24
		return $this->instance->insert_id;
263
	}
264
	/**
265
	 * @inheritdoc
266
	 */
267 1
	public function affected () {
268 1
		return $this->instance->affected_rows;
269
	}
270
	/**
271
	 * @inheritdoc
272
	 *
273
	 * @param false|mysqli_result|mysqli_stmt $query_result_stmt
274
	 */
275 27
	public function free ($query_result_stmt) {
276 27
		if ($query_result_stmt instanceof mysqli_result) {
277 21
			$query_result_stmt->free();
278
		}
279 27
		if ($query_result_stmt instanceof mysqli_stmt) {
280 9
			$query_result_stmt->free_result();
281
		}
282 27
		return true;
283
	}
284
	/**
285
	 * @inheritdoc
286
	 */
287 3
	public function columns ($table, $like = false) {
288 3
		if (!$table) {
289 1
			return false;
290
		}
291 3
		if ($like) {
292 1
			$like    = $this->s($like);
293 1
			$columns = $this->qfas("SHOW COLUMNS FROM `$table` LIKE $like") ?: [];
294
		} else {
295 3
			$columns = $this->qfas("SHOW COLUMNS FROM `$table`") ?: [];
296
		}
297 3
		return $columns;
298
	}
299
	/**
300
	 * @inheritdoc
301
	 */
302 1
	public function tables ($like = false) {
303 1
		if ($like) {
304 1
			$like = $this->s($like);
305 1
			return $this->qfas("SHOW TABLES FROM `$this->database` LIKE $like") ?: [];
306
		} else {
307 1
			return $this->qfas("SHOW TABLES FROM `$this->database`") ?: [];
308
		}
309
	}
310
	/**
311
	 * @inheritdoc
312
	 */
313 29
	protected function s_internal ($string, $single_quotes_around) {
314 29
		$return = $this->instance->real_escape_string($string);
315 29
		return $single_quotes_around ? "'$return'" : $return;
316
	}
317
	/**
318
	 * @inheritdoc
319
	 */
320 1
	public function server () {
321 1
		return $this->instance->server_info;
322
	}
323
	/**
324
	 * @inheritdoc
325
	 */
326 1
	public function __destruct () {
327 1
		if ($this->connected && is_object($this->instance)) {
328 1
			$this->instance->close();
329 1
			$this->connected = false;
330
		}
331 1
	}
332
}
333