Completed
Push — master ( 5fb3fd...336fc2 )
by Nazar
04:38
created

Config::save()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 31
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 31
rs 8.439
cc 5
eloc 17
nc 12
nop 0
1
<?php
2
/**
3
 * @package   CleverStyle CMS
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
 * Provides next events:
12
 *  System/Config/init/before
13
 *
14
 *  System/Config/init/after
15
 */
16
class Config {
17
	use
18
		Singleton;
19
	const SYSTEM_MODULE = 'System';
20
	const SYSTEM_THEME  = 'CleverStyle';
21
	/**
22
	 * Most of general configuration properties
23
	 *
24
	 * @var mixed[]
25
	 */
26
	public $core = [];
27
	/**
28
	 * Configuration of databases, except the main database, parameters of which are stored in configuration file
29
	 *
30
	 * @var mixed[]
31
	 */
32
	public $db = [];
33
	/**
34
	 * Configuration of storages, except the main storage, parameters of which are stored in configuration file
35
	 *
36
	 * @var mixed[]
37
	 */
38
	public $storage = [];
39
	/**
40
	 * Internal structure of components parameters
41
	 *
42
	 * @var mixed[]
43
	 */
44
	public $components = [];
45
	/**
46
	 * Array of all domains, which allowed to access the site
47
	 *
48
	 * Contains keys:
49
	 * * count - Total count
50
	 * * http - Insecure (http) domains
51
	 * * https - Secure (https) domains
52
	 *
53
	 * @var array
54
	 */
55
	public $mirrors = [
56
		'count' => 0,
57
		'http'  => [],
58
		'https' => []
59
	];
60
	/**
61
	 * Loading of configuration, initialization of $Config, $Cache, $L and Page objects, Routing processing
62
	 *
63
	 * @throws ExitException
64
	 */
65
	protected function construct () {
66
		Event::instance()->fire('System/Config/init/before');
67
		/**
68
		 * Reading settings from cache and defining missing data
69
		 */
70
		$config = Cache::instance()->config;
71
		/**
72
		 * Cache reloading, if necessary
73
		 */
74
		if (!is_array($config)) {
75
			$this->load_config_from_db();
76
		} else {
77
			foreach ($config as $part => $value) {
78
				$this->$part = $value;
79
			}
80
			unset($part, $value);
81
		}
82
		date_default_timezone_set($this->core['timezone']);
83
		$this->fill_mirrors();
84
		Event::instance()->fire('System/Config/init/after');
85
		if (!file_exists(MODULES.'/'.$this->core['default_module'])) {
86
			$this->core['default_module'] = self::SYSTEM_MODULE;
87
			$this->save();
88
		}
89
	}
90
	/**
91
	 * Is used to fill `$this->mirrors` using current configuration
92
	 */
93
	protected function fill_mirrors () {
94
		foreach ($this->core['url'] as $i => $address) {
95
			list($protocol, $urls) = explode('://', $address, 2);
96
			$urls                       = explode(';', $urls);
97
			$this->mirrors[$protocol][] = $urls[0];
98
		}
99
		$this->mirrors['count'] = count($this->mirrors['http']) + count($this->mirrors['https']);
100
	}
101
	/**
102
	 * Reloading of settings cache
103
	 *
104
	 * @return bool
105
	 *
106
	 * @throws ExitException
107
	 */
108
	protected function load_config_from_db () {
109
		$result = DB::instance()->qf(
110
			[
111
				"SELECT
112
					`core`,
113
					`db`,
114
					`storage`,
115
					`components`
116
				FROM `[prefix]config`
117
				WHERE `domain` = '%s'
118
				LIMIT 1",
119
				DOMAIN
120
			]
121
		);
122
		if (is_array($result)) {
123
			foreach ($result as $part => $value) {
124
				$this->$part = _json_decode($value);
125
			}
126
			unset($part, $value);
127
		} else {
128
			return false;
129
		}
130
		$this->apply_internal(false);
131
		return true;
132
	}
133
	/**
134
	 * Applying settings without saving changes into db
135
	 *
136
	 * @return bool
137
	 *
138
	 * @throws ExitException
139
	 */
140
	function apply () {
141
		return $this->apply_internal();
142
	}
143
	/**
144
	 * Applying settings without saving changes into db
145
	 *
146
	 * @param bool $cache_not_saved_mark
147
	 *
148
	 * @return bool
149
	 *
150
	 * @throws ExitException
151
	 */
152
	protected function apply_internal ($cache_not_saved_mark = true) {
153
		if ($cache_not_saved_mark) {
154
			$this->core['cache_not_saved'] = true;
155
		} else {
156
			unset($this->core['cache_not_saved']);
157
		}
158
		$Cache = Cache::instance();
159
		if (!$Cache->set(
160
			'config',
161
			[
162
				'core'       => $this->core,
163
				'db'         => $this->db,
164
				'storage'    => $this->storage,
165
				'components' => $this->components
166
			]
167
		)
168
		) {
169
			return false;
170
		}
171
		unset($Cache->{'languages'});
172
		date_default_timezone_set($this->core['timezone']);
173
		$this->fill_mirrors();
174
		Event::instance()->fire('System/Config/changed');
175
		return true;
176
	}
177
	/**
178
	 * Saving settings
179
	 *
180
	 * @return bool
181
	 *
182
	 * @throws ExitException
183
	 */
184
	function save () {
185
		if ($this->cancel_available()) {
186
			unset($this->core['cache_not_saved']);
187
		}
188
		$core_settings_keys = file_get_json(MODULES.'/System/core_settings_keys.json');
189
		foreach ($this->core as $key => $value) {
190
			if (!in_array($key, $core_settings_keys)) {
191
				unset($this->core[$key]);
192
			}
193
		}
194
		if (DB::instance()->db_prime(0)->q(
195
			"UPDATE `[prefix]config`
196
			SET
197
				`core`			= '%s',
198
				`db`			= '%s',
199
				`storage`		= '%s',
200
				`components`	= '%s'
201
			WHERE `domain` = '%s'
202
			LIMIT 1",
203
			_json_encode($this->core),
204
			_json_encode($this->db),
205
			_json_encode($this->storage),
206
			_json_encode($this->components),
207
			DOMAIN
208
		)
209
		) {
210
			$this->apply_internal(false);
211
			return true;
212
		}
213
		return false;
214
	}
215
	/**
216
	 * Whether configuration was applied (not saved) and can be canceled
217
	 *
218
	 * @return bool
219
	 */
220
	function cancel_available () {
221
		return isset($this->core['cache_not_saved']);
222
	}
223
	/**
224
	 * Canceling of applied settings
225
	 *
226
	 * @return bool
227
	 *
228
	 * @throws ExitException
229
	 */
230
	function cancel () {
231
		return $this->load_config_from_db() && $this->apply_internal(false);
232
	}
233
	/**
234
	 * Get base url of current mirror including language suffix
235
	 *
236
	 * @return string
237
	 */
238
	function base_url () {
239
		if (Route::instance()->mirror_index === -1) {
240
			return '';
241
		}
242
		/**
243
		 * @var _SERVER $_SERVER
244
		 */
245
		$base_url = "$_SERVER->protocol://$_SERVER->host";
246
		$L        = Language::instance();
247
		if ($this->core['multilingual']) {
248
			$base_url .= "/$L->clang";
249
		}
250
		return $base_url;
251
	}
252
	/**
253
	 * Get base url of main domain
254
	 *
255
	 * @return string
256
	 */
257
	function core_url () {
258
		/**
259
		 * @var _SERVER $_SERVER
260
		 */
261
		return "$_SERVER->protocol://$_SERVER->host";
262
	}
263
	/**
264
	 * Get object for getting db and storage configuration of module
265
	 *
266
	 * @param string $module_name
267
	 *
268
	 * @return Config\Module_Properties
269
	 */
270
	function module ($module_name) {
271
		if (!isset($this->components['modules'][$module_name])) {
272
			return False_class::instance();
273
		}
274
		return new Config\Module_Properties($this->components['modules'][$module_name], $module_name);
275
	}
276
}
277