Completed
Push — master ( e929f3...7e5e6c )
by Nazar
05:31
created

Config::load_configuration()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 14
nc 2
nop 0
dl 0
loc 21
ccs 15
cts 15
cp 1
crap 2
rs 9.3142
c 0
b 0
f 0
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 4
	protected function cdb () {
86 4
		return 0;
87
	}
88
	/**
89
	 * Update multilingual options when language changes
90
	 */
91 70
	protected function init () {
92 70
		$this->read_core_update_multilingual();
93 70
		Event::instance()->on(
94 70
			'System/Language/change/after',
95
			function () {
96 54
				$this->read_core_update_multilingual();
97 70
			}
98
		);
99 70
	}
100
	/**
101
	 * Loading of configuration, initialization of $Config, $Cache, $L and Page objects, Routing processing
102
	 *
103
	 * @throws ExitException
104
	 */
105 70
	protected function construct () {
106 70
		$this->cache = Cache::prefix('config');
107 70
		Event::instance()->fire('System/Config/init/before');
108 70
		$this->load_configuration();
109 70
		Event::instance()->fire('System/Config/init/after');
110 70
		if (!file_exists(MODULES.'/'.$this->core['default_module'])) {
111 2
			$this->core['default_module'] = self::SYSTEM_MODULE;
112 2
			$this->save();
113
		}
114 70
	}
115
	/**
116
	 * Reloading of settings cache
117
	 *
118
	 * @throws ExitException
119
	 */
120 70
	protected function load_configuration () {
121
		/**
122
		 * @var array[] $config
123
		 */
124 70
		$config = $this->cache->get(
125 70
			'source',
126
			function () {
127 4
				return $this->read(Core::instance()->domain);
128 70
			}
129
		);
130 70
		if (!$config) {
131 2
			throw new ExitException('Failed to load system configuration', 500);
132
		}
133 70
		$this->core_internal = $config['core'] + Options::get_defaults();
134 70
		$this->core          = $this->core_internal;
135 70
		$this->db            = $config['db'];
136 70
		$this->storage       = $config['storage'];
137 70
		$this->components    = $config['components'];
138 70
		date_default_timezone_set($this->core['timezone']);
139 70
		$this->fill_mirrors();
140 70
	}
141
	/**
142
	 * Is used to fill `$this->mirrors` using current configuration
143
	 */
144 70
	protected function fill_mirrors () {
145 70
		$this->mirrors = [
146
			'count' => 0,
147
			'http'  => [],
148
			'https' => []
149
		];
150 70
		foreach ($this->core['url'] as $i => $address) {
151 70
			list($protocol, $urls) = explode('://', $address, 2);
152 70
			$urls                       = explode(';', $urls);
153 70
			$this->mirrors[$protocol][] = $urls[0];
154
		}
155 70
		$this->mirrors['count'] = count($this->mirrors['http']) + count($this->mirrors['https']);
156 70
	}
157 70
	protected function read_core_update_multilingual () {
158 70
		$language = Language::instance(true)->clanguage ?: @$this->core['language'];
159 70
		if (!$language || $language == $this->last_language) {
160 70
			return;
161
		}
162 58
		$this->last_language  = $language;
163 58
		$multilingual_options = $this->cache->get(
164
			$language,
165 58
			function () {
166 8
				$db_id                = $this->module('System')->db('texts');
167 8
				$Text                 = Text::instance();
168 8
				$multilingual_options = [];
169 8
				foreach (Options::get_multilingual() as $option) {
170 8
					$multilingual_options[$option] = $Text->process($db_id, $this->core_internal[$option], true);
171
				}
172 8
				return $multilingual_options;
173 58
			}
174
		);
175 58
		$this->core           = $multilingual_options + $this->core;
176 58
	}
177
	/**
178
	 * Get core options item
179
	 *
180
	 * @param string[]|string[][] $item
181
	 *
182
	 * @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
183
	 *                            missing key will cause the whole thing to fail)
184
	 */
185 2
	public function core (...$item) {
186 2
		return $this->get_property_items('core', $item);
187
	}
188
	/**
189
	 * Applying settings without saving changes into db
190
	 *
191
	 * @return bool
192
	 *
193
	 * @throws ExitException
194
	 */
195 2
	public function apply () {
196 2
		$this->core = Options::apply_formatting($this->core) + Options::get_defaults();
197
		/**
198
		 * Update multilingual cache manually to avoid actually storing changes in database
199
		 */
200 2
		$multilingual_options_list = Options::get_multilingual();
201 2
		$multilingual_options      = [];
202 2
		foreach ($this->core as $option => $value) {
203 2
			if (in_array($option, $multilingual_options_list)) {
204 2
				$multilingual_options[$option] = $this->core[$option];
205
			} else {
206 2
				$this->core_internal[$option] = $value;
207
			}
208
		}
209 2
		$this->cache->set(Language::instance()->clanguage, $multilingual_options);
210 2
		return $this->apply_internal();
211
	}
212
	/**
213
	 * Applying settings without saving changes into db
214
	 *
215
	 * @param bool $cache_not_saved_mark
216
	 *
217
	 * @return bool
218
	 *
219
	 * @throws ExitException
220
	 */
221 2
	protected function apply_internal ($cache_not_saved_mark = true) {
222 2
		if ($cache_not_saved_mark) {
223 2
			$this->core_internal['cache_not_saved'] = true;
224
		} else {
225 2
			unset($this->core_internal['cache_not_saved']);
226
		}
227 2
		if (!$this->cache->set(
228 2
			'source',
229
			[
230 2
				'core'       => $this->core_internal,
231 2
				'db'         => $this->db,
232 2
				'storage'    => $this->storage,
233 2
				'components' => $this->components
234
			]
235
		)
236
		) {
237 2
			return false;
238
		}
239 2
		date_default_timezone_set($this->core['timezone']);
240 2
		$this->fill_mirrors();
241 2
		Event::instance()->fire('System/Config/changed');
242 2
		return true;
243
	}
244
	/**
245
	 * Saving settings
246
	 *
247
	 * @return bool
248
	 *
249
	 * @throws ExitException
250
	 */
251 2
	public function save () {
252 2
		unset($this->core_internal['cache_not_saved']);
253 2
		$this->core = Options::apply_formatting($this->core) + Options::get_defaults();
254
		/**
255
		 * Persist multilingual options and copy the rest to `$this->core_internal` as is
256
		 */
257 2
		$multilingual_options_list = Options::get_multilingual();
258 2
		$db_id                     = $this->module('System')->db('texts');
259 2
		$Text                      = Text::instance();
260 2
		foreach ($this->core as $option => $value) {
261 2
			if (in_array($option, $multilingual_options_list)) {
262 2
				$this->core_internal[$option] = $Text->set($db_id, 'System/Config/core', $option, $this->core[$option]);
263
			} else {
264 2
				$this->core_internal[$option] = $value;
265
			}
266
		}
267 2
		if (!$this->update(Core::instance()->domain, $this->core_internal, $this->db, $this->storage, $this->components)) {
268 2
			return false;
269
		}
270 2
		return $this->apply_internal(false);
271
	}
272
	/**
273
	 * Whether configuration was applied (not saved) and can be canceled
274
	 *
275
	 * @return bool
276
	 */
277 2
	public function cancel_available () {
278 2
		return isset($this->core_internal['cache_not_saved']);
279
	}
280
	/**
281
	 * Canceling of applied settings
282
	 *
283
	 * @throws ExitException
284
	 */
285 2
	public function cancel () {
286 2
		$this->cache->del('/');
287 2
		$this->load_configuration();
288 2
	}
289
	/**
290
	 * Get base url of current mirror including language suffix
291
	 *
292
	 * @return string
293
	 */
294 8
	public function base_url () {
295 8
		if (Request::instance()->mirror_index === -1) {
296 2
			return '';
297
		}
298 8
		$base_url = $this->core_url();
299 8
		if ($this->core['multilingual']) {
300 4
			$L = Language::instance();
301 4
			$base_url .= "/$L->clang";
302
		}
303 8
		return $base_url;
304
	}
305
	/**
306
	 * Get base url of main domain
307
	 *
308
	 * @return string
309
	 */
310 20
	public function core_url () {
311 20
		$Request = Request::instance();
312 20
		return "$Request->scheme://$Request->host";
313
	}
314
	/**
315
	 * Get object for getting db and storage configuration of module
316
	 *
317
	 * @param string $module_name
318
	 *
319
	 * @return Config\Module_Properties
320
	 */
321 50
	public function module ($module_name) {
322 50
		if (!isset($this->components['modules'][$module_name])) {
323
			/** @noinspection PhpIncompatibleReturnTypeInspection */
324 2
			return False_class::instance();
325
		}
326 50
		return new Config\Module_Properties($this->components['modules'][$module_name], $module_name);
327
	}
328
}
329