Completed
Push — master ( 5c0dbc...42c9a9 )
by Nazar
04:27
created

DB::connecting()   C

Complexity

Conditions 10
Paths 9

Size

Total Lines 62
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 11.1512

Importance

Changes 0
Metric Value
cc 10
eloc 33
nc 9
nop 2
dl 0
loc 62
ccs 24
cts 31
cp 0.7742
crap 11.1512
rs 6.4192
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * @package   CleverStyle Framework
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs;
9
10
/**
11
 * @method static $this instance($check = false)
12
 */
13
class DB {
14
	use
15
		Singleton;
16
	const CONNECTIONS_ALL           = null;
17
	const CONNECTIONS_FAILED        = 0;
18
	const CONNECTIONS_SUCCESSFUL    = 1;
19
	const CONNECTIONS_MIRRORS       = 'mirror';
20
	const MASTER_MIRROR             = -1;
21
	const MIRROR_MODE_MASTER_MASTER = 0;
22
	const MIRROR_MODE_MASTER_SLAVE  = 1;
23
	/**
24
	 * @var DB\_Abstract[]
25
	 */
26
	protected $connections = [];
27
	/**
28
	 * @var array
29
	 */
30
	protected $successful_connections = [];
31
	/**
32
	 * @var array
33
	 */
34
	protected $failed_connections = [];
35
	/**
36
	 * @var array
37
	 */
38
	protected $mirrors = [];
39
	/**
40
	 * Get list of connections of specified type
41
	 *
42
	 * @param bool|null|string $type One of constants `self::CONNECTIONS_*`
43
	 *
44
	 * @return array For `self::CONNECTIONS_ALL` array of successful connections with corresponding objects as values of array<br>
45
	 *               Otherwise array where keys are database ids and values are strings with information about database
46
	 */
47
	function get_connections_list ($type = self::CONNECTIONS_ALL) {
48
		if ($type == self::CONNECTIONS_FAILED) {
49
			return $this->failed_connections;
50
		}
51
		if ($type == self::CONNECTIONS_SUCCESSFUL) {
52
			return $this->successful_connections;
53
		}
54
		if ($type == self::CONNECTIONS_MIRRORS) {
55
			return $this->mirrors;
56
		}
57
		return $this->connections;
58
	}
59
	/**
60
	 * Total number of executed queries
61
	 *
62
	 * @return int
63
	 */
64 2
	function queries () {
65 2
		$queries = 0;
66 2
		foreach ($this->connections as $c) {
67 2
			$queries += $c->queries()['num'];
68
		}
69 2
		return $queries;
70
	}
71
	/**
72
	 * Total time spent on all queries and connections
73
	 *
74
	 * @return float
75
	 */
76 2
	function time () {
77 2
		$time = 0;
78 2
		foreach ($this->connections as $c) {
79 2
			$time += $c->connecting_time() + $c->time();
80
		}
81 2
		return $time;
82
	}
83
	/**
84
	 * Get database instance for read queries
85
	 *
86
	 * @param int $database_id
87
	 *
88
	 * @return DB\_Abstract|False_class Returns instance of False_class on failure
89
	 *
90
	 * @throws ExitException
91
	 */
92 38
	function db ($database_id) {
93 38
		return $this->generic_connecting($database_id, true);
94
	}
95
	/**
96
	 * Get database instance for write queries
97
	 *
98
	 * @param int $database_id
99
	 *
100
	 * @return DB\_Abstract|False_class Returns instance of False_class on failure
101
	 *
102
	 * @throws ExitException
103
	 */
104 40
	function db_prime ($database_id) {
105 40
		return $this->generic_connecting($database_id, false);
106
	}
107
	/**
108
	 * @param int  $database_id
109
	 * @param bool $read_query
110
	 *
111
	 * @return DB\_Abstract|False_class
112
	 *
113
	 * @throws ExitException
114
	 */
115 48
	protected function generic_connecting ($database_id, $read_query) {
116 48
		if (!is_int($database_id) && $database_id != '0') {
117
			return False_class::instance();
118
		}
119
		/**
120
		 * Establish wne connection to the database
121
		 */
122 48
		$connection = $this->connecting($database_id, $read_query);
123
		/**
124
		 * If connection fails - try once more
125
		 */
126 48
		if (!$connection) {
127
			$connection = $this->connecting($database_id, $read_query);
128
		}
129
		/**
130
		 * If failed twice - show error
131
		 */
132 48
		if (!$connection) {
133
			throw new ExitException(500);
134
		}
135 48
		return $connection;
136
	}
137
	/**
138
	 * Processing of all DB request
139
	 *
140
	 * @param int  $database_id
141
	 * @param bool $read_query
142
	 *
143
	 * @return DB\_Abstract|False_class
144
	 */
145 48
	protected function connecting ($database_id, $read_query = true) {
146
		/**
147
		 * If connection found in list of failed connections - return instance of False_class
148
		 */
149 48
		if (isset($this->failed_connections[$database_id])) {
150
			return False_class::instance();
151
		}
152
		/**
153
		 * If connection to DB mirror already established
154
		 */
155
		if (
156 48
			$read_query &&
157 48
			isset($this->mirrors[$database_id])
158
		) {
159
			return $this->mirrors[$database_id];
160
		}
161
		/**
162
		 * If connection already established
163
		 */
164 48
		if (isset($this->connections[$database_id])) {
165 38
			return $this->connections[$database_id];
166
		}
167 48
		$Core = Core::instance();
168 48
		list($database_settings, $is_mirror) = $this->get_db_connection_settings($database_id, $read_query);
169
		/**
170
		 * Establish new connection
171
		 *
172
		 * @var DB\_Abstract $connection
173
		 */
174 48
		$engine_class    = "cs\\DB\\$database_settings[type]";
175 48
		$connection      = new $engine_class(
176 48
			$database_settings['name'],
177 48
			$database_settings['user'],
178 48
			$database_settings['password'],
179 48
			$database_settings['host'],
180 48
			$database_settings['prefix']
181
		);
182 48
		$connection_name = ($database_id == 0 ? "Core DB ($Core->db_type)" : $database_id)."/$database_settings[host]/$database_settings[type]";
183 48
		unset($engine_class, $database_settings);
184
		/**
185
		 * If successfully - add connection to the list of success connections and return instance of DB engine object
186
		 */
187 48
		if (is_object($connection) && $connection->connected()) {
188 48
			$this->successful_connections[] = $connection_name;
189 48
			$this->$database_id             = $connection;
190 48
			if ($is_mirror) {
191 2
				$this->mirrors[$database_id] = $connection;
192
			} else {
193 46
				$this->connections[$database_id] = $connection;
194
			}
195 48
			return $connection;
196
		}
197
		/**
198
		 * If failed - add connection to the list of failed connections and log error
199
		 */
200
		$this->failed_connections[$database_id] = $connection_name;
201
		trigger_error(
202
			$database_id == 0 ? 'Error connecting to core DB of site' : "Error connecting to DB $connection_name",
203
			E_USER_ERROR
204
		);
205
		return False_class::instance();
206
	}
207
	/**
208
	 * Get database connection settings, depending on query type and system configuration settings of main db or one of mirrors might be returned
209
	 *
210
	 * @param int  $database_id
211
	 * @param bool $read_query
212
	 *
213
	 * @return array
214
	 */
215 48
	protected function get_db_connection_settings ($database_id, $read_query) {
216 48
		$Config = Config::instance();
217 48
		$Core   = Core::instance();
218
		/**
219
		 * Choose right mirror depending on system configuration
220
		 */
221 48
		$is_mirror    = false;
222 48
		$mirror_index = $this->choose_mirror($database_id, $read_query);
223 48
		if ($mirror_index === self::MASTER_MIRROR) {
224 46
			if ($database_id == 0) {
225
				$database_settings = [
226 46
					'type'     => $Core->db_type,
227 46
					'name'     => $Core->db_name,
228 46
					'user'     => $Core->db_user,
229 46
					'password' => $Core->db_password,
230 46
					'host'     => $Core->db_host,
231 46
					'prefix'   => $Core->db_prefix
232
				];
233
			} else {
234 46
				$database_settings = $Config->db[$database_id];
235
			}
236
		} else {
237 2
			$database_settings = $Config->db[$database_id]['mirrors'][$mirror_index];
238 2
			$is_mirror         = true;
239
		}
240
		return [
241 48
			$database_settings,
242 48
			$is_mirror
243
		];
244
	}
245
	/**
246
	 * Choose index of DB mirrors among available
247
	 *
248
	 * @param int  $database_id
249
	 * @param bool $read_query
250
	 *
251
	 * @return int
252
	 */
253 48
	protected function choose_mirror ($database_id, $read_query = true) {
254 48
		$Config = Config::instance(true);
255
		/**
256
		 * $Config might be not initialized, so, check also for `$Config->core`
257
		 */
258
		if (
259 48
			!@$Config->core['db_balance'] ||
260 48
			!isset($Config->db[$database_id]['mirrors'][0])
261
		) {
262 46
			return self::MASTER_MIRROR;
263
		}
264 2
		$mirrors_count = count($Config->db[$database_id]['mirrors']);
265
		/**
266
		 * Main db should be excluded from read requests if writes to mirrors are not allowed
267
		 */
268 2
		$selected_mirror = mt_rand(
269 2
			0,
270 2
			$read_query && $Config->core['db_mirror_mode'] == self::MIRROR_MODE_MASTER_SLAVE ? $mirrors_count - 1 : $mirrors_count
271
		);
272
		/**
273
		 * Main DB assumed to be in the end of interval, that is why `$select_mirror < $mirrors_count` will correspond to one of available mirrors,
274
		 * and `$select_mirror == $mirrors_count` to master DB itself
275
		 */
276 2
		return $selected_mirror < $mirrors_count ? $selected_mirror : self::MASTER_MIRROR;
277
	}
278
}
279