Completed
Push — master ( 4e0caf...a1e1e6 )
by Nazar
04:44
created

Config   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 314
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 14

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
dl 0
loc 314
ccs 121
cts 121
cp 1
rs 9.3999
c 0
b 0
f 0
wmc 33
lcom 1
cbo 14

15 Methods

Rating   Name   Duplication   Size   Complexity  
A cdb() 0 3 1
A fill_mirrors() 0 13 2
A init() 0 8 1
A construct() 0 10 2
A load_configuration() 0 22 2
B read_core_update_multilingual() 0 23 6
A core() 0 3 1
A apply() 0 17 3
A apply_internal() 0 23 3
B save() 0 22 4
A cancel_available() 0 3 1
A cancel() 0 4 1
A base_url() 0 11 3
A core_url() 0 4 1
A module() 0 7 2
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
use
10
	cs\Config\Options;
11
12
/**
13
 * Provides next events:
14
 *  System/Config/init/before
15
 *
16
 *  System/Config/init/after
17
 *
18
 *  System/Config/changed
19
 *
20
 * @method static $this instance($check = false)
21
 */
22
class Config {
23
	use
24
		CRUD,
25
		Properties_getter,
26
		Singleton;
27
	const INIT_STATE_METHOD = 'init';
28
	const SYSTEM_MODULE     = 'System';
29
	const SYSTEM_THEME      = 'CleverStyle';
30
	/**
31
	 * @var Cache\Prefix
32
	 */
33
	protected $cache;
34
	/**
35
	 * Most of general configuration properties
36
	 *
37
	 * @var array
38
	 */
39
	public $core = [];
40
	/**
41
	 * @var array
42
	 */
43
	protected $core_internal = [];
44
	/**
45
	 * @var string
46
	 */
47
	protected $last_language;
48
	/**
49
	 * Configuration of databases, except the main database, parameters of which are stored in configuration file
50
	 *
51
	 * @var mixed[]
52
	 */
53
	public $db = [];
54
	/**
55
	 * Configuration of storages, except the main storage, parameters of which are stored in configuration file
56
	 *
57
	 * @var mixed[]
58
	 */
59
	public $storage = [];
60
	/**
61
	 * Internal structure of components parameters
62
	 *
63
	 * @var array[]
64
	 */
65
	public $components = [];
66
	/**
67
	 * Array of all domains, which allowed to access the site
68
	 *
69
	 * Contains keys:
70
	 * * count - Total count
71
	 * * http - Insecure (http) domains
72
	 * * https - Secure (https) domains
73
	 *
74
	 * @var array
75
	 */
76
	public    $mirrors;
77
	protected $data_model = [
78
		'domain'     => 'text',
79
		'core'       => 'json',
80
		'db'         => 'json',
81
		'storage'    => 'json',
82
		'components' => 'json'
83
	];
84
	protected $table      = '[prefix]config';
85 6
	protected function cdb () {
86 6
		return 0;
87
	}
88
	/**
89
	 * Update multilingual options when language changes
90
	 */
91 72
	protected function init () {
92 72
		Event::instance()->on(
93 72
			'System/Language/change/after',
94
			function () {
95 54
				$this->read_core_update_multilingual();
96 72
			}
97
		);
98 72
	}
99
	/**
100
	 * Loading of configuration, initialization of $Config, $Cache, $L and Page objects, Routing processing
101
	 *
102
	 * @throws ExitException
103
	 */
104 72
	protected function construct () {
105 72
		$this->cache = Cache::prefix('config');
106 72
		Event::instance()->fire('System/Config/init/before');
107 72
		$this->load_configuration();
108 72
		Event::instance()->fire('System/Config/init/after');
109 72
		if (!file_exists(MODULES.'/'.$this->core['default_module'])) {
110 2
			$this->core['default_module'] = self::SYSTEM_MODULE;
111 2
			$this->save();
112
		}
113 72
	}
114
	/**
115
	 * Reloading of settings cache
116
	 *
117
	 * @throws ExitException
118
	 */
119 72
	protected function load_configuration () {
120
		/**
121
		 * @var array[] $config
122
		 */
123 72
		$config = $this->cache->get(
124 72
			'source',
125
			function () {
126 4
				return $this->read(Core::instance()->domain);
127 72
			}
128
		);
129 72
		if (!$config) {
130 2
			throw new ExitException('Failed to load system configuration', 500);
131
		}
132 72
		$this->core_internal = $config['core'] + Options::get_defaults();
133 72
		$this->core          = $this->core_internal;
134 72
		$this->db            = $config['db'];
135 72
		$this->storage       = $config['storage'];
136 72
		$this->components    = $config['components'];
137 72
		date_default_timezone_set($this->core['timezone']);
138 72
		$this->fill_mirrors();
139 72
		$this->read_core_update_multilingual(true);
140 72
	}
141
	/**
142
	 * Is used to fill `$this->mirrors` using current configuration
143
	 */
144 72
	protected function fill_mirrors () {
145 72
		$this->mirrors = [
146
			'count' => 0,
147
			'http'  => [],
148
			'https' => []
149
		];
150 72
		foreach ($this->core['url'] as $i => $address) {
151 72
			list($protocol, $urls) = explode('://', $address, 2);
152 72
			$urls                       = explode(';', $urls);
153 72
			$this->mirrors[$protocol][] = $urls[0];
154
		}
155 72
		$this->mirrors['count'] = count($this->mirrors['http']) + count($this->mirrors['https']);
156 72
	}
157
	/**
158
	 * @param bool $force
159
	 */
160 72
	protected function read_core_update_multilingual ($force = false) {
161 72
		if ($force) {
162 72
			$this->last_language = null;
163
		}
164 72
		$language = Language::instance()->clanguage ?: @$this->core['language'];
165 72
		if (!$language || $language == $this->last_language) {
166 54
			return;
167
		}
168 72
		$this->last_language  = $language;
169 72
		$multilingual_options = $this->cache->get(
170
			$language,
171 72
			function () {
172 12
				$db_id                = $this->module('System')->db('texts');
173 12
				$Text                 = Text::instance();
174 12
				$multilingual_options = [];
175 12
				foreach (Options::get_multilingual() as $option) {
176 12
					$multilingual_options[$option] = $Text->process($db_id, $this->core_internal[$option], true);
177
				}
178 12
				return $multilingual_options;
179 72
			}
180
		);
181 72
		$this->core           = $multilingual_options + $this->core;
182 72
	}
183
	/**
184
	 * Get core options item
185
	 *
186
	 * @param string[]|string[][] $item
187
	 *
188
	 * @return mixed|mixed[]|null Core options items (or associative array of items) if exists or `null` otherwise (in case if `$item` is an array even one
189
	 *                            missing key will cause the whole thing to fail)
190
	 */
191 2
	public function core (...$item) {
192 2
		return $this->get_property_items('core', $item);
193
	}
194
	/**
195
	 * Applying settings without saving changes into db
196
	 *
197
	 * @return bool
198
	 *
199
	 * @throws ExitException
200
	 */
201 2
	public function apply () {
202 2
		$this->core = Options::apply_formatting($this->core) + Options::get_defaults();
203
		/**
204
		 * Update multilingual cache manually to avoid actually storing changes in database
205
		 */
206 2
		$multilingual_options_list = Options::get_multilingual();
207 2
		$multilingual_options      = [];
208 2
		foreach ($this->core as $option => $value) {
209 2
			if (in_array($option, $multilingual_options_list)) {
210 2
				$multilingual_options[$option] = $this->core[$option];
211
			} else {
212 2
				$this->core_internal[$option] = $value;
213
			}
214
		}
215 2
		$this->cache->set(Language::instance()->clanguage, $multilingual_options);
216 2
		return $this->apply_internal();
217
	}
218
	/**
219
	 * Applying settings without saving changes into db
220
	 *
221
	 * @param bool $cache_not_saved_mark
222
	 *
223
	 * @return bool
224
	 *
225
	 * @throws ExitException
226
	 */
227 4
	protected function apply_internal ($cache_not_saved_mark = true) {
228 4
		if ($cache_not_saved_mark) {
229 2
			$this->core_internal['cache_not_saved'] = true;
230
		} else {
231 4
			unset($this->core_internal['cache_not_saved']);
232
		}
233 4
		if (!$this->cache->set(
234 4
			'source',
235
			[
236 4
				'core'       => $this->core_internal,
237 4
				'db'         => $this->db,
238 4
				'storage'    => $this->storage,
239 4
				'components' => $this->components
240
			]
241
		)
242
		) {
243 2
			return false;
244
		}
245 4
		date_default_timezone_set($this->core['timezone']);
246 4
		$this->fill_mirrors();
247 4
		Event::instance()->fire('System/Config/changed');
248 4
		return true;
249
	}
250
	/**
251
	 * Saving settings
252
	 *
253
	 * @return bool
254
	 *
255
	 * @throws ExitException
256
	 */
257 4
	public function save () {
258 4
		unset($this->core_internal['cache_not_saved']);
259 4
		$this->core = Options::apply_formatting($this->core) + Options::get_defaults();
260
		/**
261
		 * Persist multilingual options and copy the rest to `$this->core_internal` as is
262
		 */
263 4
		$multilingual_options_list = Options::get_multilingual();
264 4
		$db_id                     = $this->module('System')->db('texts');
265 4
		$Text                      = Text::instance();
266 4
		foreach ($this->core as $option => $value) {
267 4
			if (in_array($option, $multilingual_options_list)) {
268 4
				$this->core_internal[$option] = $Text->set($db_id, 'System/Config/core', $option, $this->core[$option]);
269
			} else {
270 4
				$this->core_internal[$option] = $value;
271
			}
272
		}
273 4
		if (!$this->update(Core::instance()->domain, $this->core_internal, $this->db, $this->storage, $this->components)) {
274 2
			return false;
275
		}
276 4
		$this->cache->del('/');
277 4
		return $this->apply_internal(false);
278
	}
279
	/**
280
	 * Whether configuration was applied (not saved) and can be canceled
281
	 *
282
	 * @return bool
283
	 */
284 2
	public function cancel_available () {
285 2
		return isset($this->core_internal['cache_not_saved']);
286
	}
287
	/**
288
	 * Canceling of applied settings
289
	 *
290
	 * @throws ExitException
291
	 */
292 2
	public function cancel () {
293 2
		$this->cache->del('/');
294 2
		$this->load_configuration();
295 2
	}
296
	/**
297
	 * Get base url of current mirror including language suffix
298
	 *
299
	 * @return string
300
	 */
301 8
	public function base_url () {
302 8
		if (Request::instance()->mirror_index === -1) {
303 2
			return '';
304
		}
305 8
		$base_url = $this->core_url();
306 8
		if ($this->core['multilingual']) {
307 4
			$L = Language::instance();
308 4
			$base_url .= "/$L->clang";
309
		}
310 8
		return $base_url;
311
	}
312
	/**
313
	 * Get base url of main domain
314
	 *
315
	 * @return string
316
	 */
317 20
	public function core_url () {
318 20
		$Request = Request::instance();
319 20
		return "$Request->scheme://$Request->host";
320
	}
321
	/**
322
	 * Get object for getting db and storage configuration of module
323
	 *
324
	 * @param string $module_name
325
	 *
326
	 * @return Config\Module_Properties
327
	 */
328 52
	public function module ($module_name) {
329 52
		if (!isset($this->components['modules'][$module_name])) {
330
			/** @noinspection PhpIncompatibleReturnTypeInspection */
331 2
			return False_class::instance();
332
		}
333 52
		return new Config\Module_Properties($this->components['modules'][$module_name], $module_name);
334
	}
335
}
336