Completed
Push — master ( a8898a...5c0dbc )
by Nazar
04:15
created

DB::connecting()   C

Complexity

Conditions 10
Paths 9

Size

Total Lines 62
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 11.7193

Importance

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

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
	function queries () {
65
		$queries = 0;
66
		foreach ($this->connections as $c) {
67
			$queries += $c->queries()['num'];
68
		}
69
		return $queries;
70
	}
71
	/**
72
	 * Total time spent on all queries and connections
73
	 *
74
	 * @return float
75
	 */
76
	function time () {
77
		$time = 0;
78
		foreach ($this->connections as $c) {
79
			$time += $c->connecting_time() + $c->time();
80
		}
81
		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 4
	function db ($database_id) {
93 4
		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 2
	function db_prime ($database_id) {
105 2
		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 6
	protected function generic_connecting ($database_id, $read_query) {
116 6
		if (!is_int($database_id) && $database_id != '0') {
117
			return False_class::instance();
118
		}
119
		/**
120
		 * Establish wne connection to the database
121
		 */
122 6
		$connection = $this->connecting($database_id, $read_query);
123
		/**
124
		 * If connection fails - try once more
125
		 */
126 6
		if (!$connection) {
127
			$connection = $this->connecting($database_id, $read_query);
128
		}
129
		/**
130
		 * If failed twice - show error
131
		 */
132 6
		if (!$connection) {
133
			throw new ExitException(500);
134
		}
135 6
		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 6
	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 6
		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 6
			$read_query &&
157 6
			isset($this->mirrors[$database_id])
158
		) {
159
			return $this->mirrors[$database_id];
160
		}
161
		/**
162
		 * If connection already established
163
		 */
164 6
		if (isset($this->connections[$database_id])) {
165
			return $this->connections[$database_id];
166
		}
167 6
		$Core = Core::instance();
168 6
		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 6
		$engine_class    = "cs\\DB\\$database_settings[type]";
175 6
		$connection      = new $engine_class(
176 6
			$database_settings['name'],
177 6
			$database_settings['user'],
178 6
			$database_settings['password'],
179 6
			$database_settings['host'],
180 6
			$database_settings['prefix']
181
		);
182 6
		$connection_name = ($database_id == 0 ? "Core DB ($Core->db_type)" : $database_id)."/$database_settings[host]/$database_settings[type]";
183 6
		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 6
		if (is_object($connection) && $connection->connected()) {
188 6
			$this->successful_connections[] = $connection_name;
189 6
			$this->$database_id             = $connection;
190 6
			if ($is_mirror) {
191 2
				$this->mirrors[$database_id] = $connection;
192
			} else {
193 4
				$this->connections[$database_id] = $connection;
194
			}
195 6
			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 6
	protected function get_db_connection_settings ($database_id, $read_query) {
216 6
		$Config = Config::instance();
217 6
		$Core   = Core::instance();
218
		/**
219
		 * Choose right mirror depending on system configuration
220
		 */
221 6
		$is_mirror    = false;
222 6
		$mirror_index = $this->choose_mirror($database_id, $read_query);
223 6
		if ($mirror_index === self::MASTER_MIRROR) {
224 4
			if ($database_id == 0) {
225
				$database_settings = [
226 4
					'type'     => $Core->db_type,
227 4
					'name'     => $Core->db_name,
228 4
					'user'     => $Core->db_user,
229 4
					'password' => $Core->db_password,
230 4
					'host'     => $Core->db_host,
231 4
					'prefix'   => $Core->db_prefix
232
				];
233
			} else {
234 4
				$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 6
			$database_settings,
242 6
			$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 6
	protected function choose_mirror ($database_id, $read_query = true) {
254 6
		$Config = Config::instance(true);
255
		/**
256
		 * $Config might be not initialized, so, check also for `$Config->core`
257
		 */
258
		if (
259 6
			!@$Config->core['db_balance'] ||
260 6
			!isset($Config->db[$database_id]['mirrors'][0])
261
		) {
262 4
			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