Completed
Push — master ( cb33fb...bc1d9b )
by Nazar
13:14
created

MySQLi   C

Complexity

Total Complexity 67

Size/Duplication

Total Lines 302
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 1

Test Coverage

Coverage 70.74%

Importance

Changes 0
Metric Value
dl 0
loc 302
ccs 133
cts 188
cp 0.7074
rs 5.7097
c 0
b 0
f 0
wmc 67
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 6 1
A f() 0 9 3
B f_result() 0 16 9
B f_stmt() 0 21 6
A f_stmt_bind_result() 0 3 1
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 27
		return $stmt->bind_param(
133 27
			str_repeat('s', count($local_parameters)),
134 27
			...$local_parameters
135
		);
136
	}
137
	/**
138
	 * @inheritdoc
139
	 *
140
	 * @param false|mysqli_result|mysqli_stmt $query_result_stmt
141
	 */
142 32
	public function f ($query_result_stmt, $single_column = false, $array = false, $indexed = false) {
143 32
		if ($query_result_stmt instanceof mysqli_result) {
144 30
			return $this->f_result($query_result_stmt, $single_column, $array, $indexed);
145
		}
146 25
		if ($query_result_stmt instanceof mysqli_stmt) {
147 25
			return $this->f_stmt($query_result_stmt, $single_column, $array, $indexed);
148
		}
149 1
		return false;
150
	}
151
	/**
152
	 * @param mysqli_result $query_result
153
	 * @param bool          $single_column
154
	 * @param bool          $array
155
	 * @param bool          $indexed
156
	 *
157
	 * @return array|bool|mixed
158
	 */
159 30
	protected function f_result ($query_result, $single_column, $array, $indexed) {
160 30
		$result_type = $single_column || $indexed ? MYSQLI_NUM : MYSQLI_ASSOC;
161 30
		if ($array) {
162 21
			$result = [];
163 21
			while ($current = $query_result->fetch_array($result_type)) {
164 20
				$result[] = $single_column ? $current[0] : $current;
165
			}
166 21
			$this->free($query_result);
167 21
			return $result;
168
		}
169 26
		$result = $query_result->fetch_array($result_type);
170 26
		if ($result === null) {
171 22
			$result = false;
172
		}
173 26
		return $single_column && $result ? $result[0] : $result;
174
	}
175
	/**
176
	 * @param mysqli_stmt $stmt
177
	 * @param bool        $single_column
178
	 * @param bool        $array
179
	 * @param bool        $indexed
180
	 *
181
	 * @return array|bool|mixed
182
	 */
183 25
	protected function f_stmt ($stmt, $single_column, $array, $indexed) {
184 25
		$meta    = $stmt->result_metadata();
185 25
		$result  = [];
186 25
		$columns = [];
187 25
		while ($field = $meta->fetch_field()) {
188 25
			$result[]  = null;
189 25
			$columns[] = $field->name;
190
		}
191 25
		if (!$stmt->store_result() || !$this->f_stmt_bind_result($stmt, $result)) {
192
			return false;
193
		}
194 25
		if ($array) {
195 9
			$return_result = [];
196 9
			while ($current_result = $this->f_stmt_internal($stmt, $result, $single_column, $indexed, $columns)) {
197 9
				$return_result[] = $current_result;
198
			}
199 9
			$this->free($stmt);
200 9
			return $return_result;
201
		}
202 25
		return $this->f_stmt_internal($stmt, $result, $single_column, $indexed, $columns);
203
	}
204
	/**
205
	 * @param mysqli_stmt $stmt
206
	 * @param array       $result
207
	 *
208
	 * @return bool
209
	 */
210 25
	protected function f_stmt_bind_result ($stmt, &$result) {
211 25
		return $stmt->bind_result(...$result);
212
	}
213
	/**
214
	 * @param mysqli_stmt $stmt
215
	 * @param array       $result
216
	 * @param bool        $single_column
217
	 * @param bool        $indexed
218
	 * @param string[]    $columns
219
	 *
220
	 * @return array|bool
221
	 */
222 25
	protected function f_stmt_internal ($stmt, $result, $single_column, $indexed, $columns) {
223 25
		if (!$stmt->fetch()) {
224 10
			return false;
225
		}
226 25
		if ($single_column && $result) {
227 8
			return $result[0];
228
		}
229
		// Hack: `$result`'s values are all references, we need to dereference them into plain values
230 24
		$new_result = [];
231 24
		foreach ($result as $i => $v) {
232 24
			$new_result[$i] = $v;
233
		}
234 24
		$result = $new_result;
235 24
		if ($indexed) {
236 1
			return $result;
237
		}
238 24
		return array_combine($columns, $result);
239
	}
240
	/**
241
	 * @inheritdoc
242
	 */
243 24
	public function id () {
244 24
		return $this->instance->insert_id;
245
	}
246
	/**
247
	 * @inheritdoc
248
	 */
249 1
	public function affected () {
250 1
		return $this->instance->affected_rows;
251
	}
252
	/**
253
	 * @inheritdoc
254
	 *
255
	 * @param false|mysqli_result|mysqli_stmt $query_result_stmt
256
	 */
257 27
	public function free ($query_result_stmt) {
258 27
		if ($query_result_stmt instanceof mysqli_result) {
259 21
			$query_result_stmt->free();
260
		}
261 27
		if ($query_result_stmt instanceof mysqli_stmt) {
262 9
			$query_result_stmt->free_result();
263
		}
264 27
		return true;
265
	}
266
	/**
267
	 * @inheritdoc
268
	 */
269 3
	public function columns ($table, $like = false) {
270 3
		if (!$table) {
271 1
			return false;
272
		}
273 3
		if ($like) {
274 1
			$like    = $this->s($like);
275 1
			$columns = $this->qfas("SHOW COLUMNS FROM `$table` LIKE $like") ?: [];
276
		} else {
277 3
			$columns = $this->qfas("SHOW COLUMNS FROM `$table`") ?: [];
278
		}
279 3
		return $columns;
280
	}
281
	/**
282
	 * @inheritdoc
283
	 */
284 1
	public function tables ($like = false) {
285 1
		if ($like) {
286 1
			$like = $this->s($like);
287 1
			return $this->qfas("SHOW TABLES FROM `$this->database` LIKE $like") ?: [];
288
		} else {
289 1
			return $this->qfas("SHOW TABLES FROM `$this->database`") ?: [];
290
		}
291
	}
292
	/**
293
	 * @inheritdoc
294
	 */
295 29
	protected function s_internal ($string, $single_quotes_around) {
296 29
		$return = $this->instance->real_escape_string($string);
297 29
		return $single_quotes_around ? "'$return'" : $return;
298
	}
299
	/**
300
	 * @inheritdoc
301
	 */
302 1
	public function server () {
303 1
		return $this->instance->server_info;
304
	}
305
	/**
306
	 * @inheritdoc
307
	 */
308 1
	public function __destruct () {
309 1
		if ($this->connected && is_object($this->instance)) {
310 1
			$this->instance->close();
311 1
			$this->connected = false;
312
		}
313 1
	}
314
}
315