Completed
Push — master ( 355c65...a3fef0 )
by Nazar
04:48
created

Installer::initialize_filesystem()   C

Complexity

Conditions 8
Paths 1

Size

Total Lines 36
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 8.0052

Importance

Changes 0
Metric Value
cc 8
eloc 23
nc 1
nop 1
dl 0
loc 36
ccs 22
cts 23
cp 0.9565
crap 8.0052
rs 5.3846
c 0
b 0
f 0
1
<?php
2
/**
3
 * @package    CleverStyle Framework
4
 * @subpackage Installer
5
 * @author     Nazar Mokrynskyi <[email protected]>
6
 * @copyright  Copyright (c) 2016, Nazar Mokrynskyi
7
 * @license    MIT License, see license.txt
8
 */
9
namespace cs;
10
use
11
	RuntimeException;
12
13
class Installer {
14
	const MAIN_CONFIG_STUB = /** @lang JSON */
15
		<<<CONFIG
16
{
17
//Domain of main mirror
18
	"domain"			: "@domain",
19
//Base timezone
20
	"timezone"			: "@timezone",
21
//Settings of main DB
22
	"db_host"			: "@db_host",
23
	"db_type"			: "@db_type",
24
	"db_name"			: "@db_name",
25
	"db_user"			: "@db_user",
26
	"db_password"		: "@db_password",
27
	"db_prefix"			: "@db_prefix",
28
//Settings of main Storage
29
	"storage_type"		: "Local",
30
	"storage_url"		: "",
31
	"storage_host"		: "localhost",
32
	"storage_user"		: "",
33
	"storage_password"	: "",
34
//Base language
35
	"language"			: "@language",
36
//Cache engine
37
	"cache_engine"		: "FileSystem",
38
//Settings of Memcached cache engine
39
	"memcache_host"		: "127.0.0.1",
40
	"memcache_port"		: "11211",
41
//Any length
42
	"public_key"		: "@public_key"
43
}
44
45
CONFIG;
46
	/**
47
	 * @param string $source
48
	 * @param string $target
49
	 * @param string $site_name
50
	 * @param string $url
51
	 * @param string $timezone
52
	 * @param string $db_host
53
	 * @param string $db_engine
54
	 * @param string $db_name
55
	 * @param string $db_user
56
	 * @param string $db_password
57
	 * @param string $db_prefix
58
	 * @param string $language
59
	 * @param string $admin_email
60
	 * @param string $admin_password
61
	 * @param int    $mode
62
	 *
63
	 * @throws RuntimeException
64
	 */
65 4
	public static function install (
66
		$source,
67
		$target,
68
		$site_name,
69
		$url,
70
		$timezone,
71
		$db_host,
72
		$db_engine,
73
		$db_name,
74
		$db_user,
75
		$db_password,
76
		$db_prefix,
77
		$language,
78
		$admin_email,
79
		$admin_password,
80
		$mode
81
	) {
82 4
		static::pre_installation_checks($source, $target, $db_engine);
83
		// Needed to be defined before connecting to the database
84 4
		defined('DEBUG') || define('DEBUG', false);
85 4
		$file_index_map = static::initialize_filesystem($source);
86 4
		static::extract($file_index_map, $source, $target);
87 4
		$domain     = parse_url($url, PHP_URL_HOST);
88 4
		$public_key = hash('sha512', random_bytes(1000));
89 4
		static::initialize_core_config(
90
			$target,
91
			$domain,
92
			$timezone,
93
			$db_host,
94
			$db_engine,
95
			$db_name,
96
			$db_user,
97
			$db_password,
98
			$db_prefix,
99
			$language,
100
			$public_key
101
		);
102
		/**
103
		 * @var \cs\DB\_Abstract $cdb
104
		 */
105 4
		$cdb = "cs\\DB\\$db_engine";
106 4
		$cdb = new $cdb($db_name, $db_user, $db_password, $db_host, $db_prefix);
107 4
		if (!is_object($cdb) || !$cdb->connected()) {
108
			throw new RuntimeException("Can't connect to database! Installation aborted.");
109
		}
110 4
		static::initialize_db_structure($cdb, $source, $db_engine);
111 4
		static::initialize_system_config($cdb, $source, $site_name, $url, $admin_email, $language, $domain, $timezone, $mode);
112 4
		static::create_root_administrator($cdb, $admin_email, $admin_password, $public_key);
113 4
		unset($cdb);
114 4
	}
115
	/**
116
	 * @param string $source
117
	 * @param string $target
118
	 * @param string $db_engine
119
	 *
120
	 * @throws RuntimeException
121
	 */
122 4
	protected static function pre_installation_checks ($source, $target, $db_engine) {
123 4
		if (file_exists("$target/config/main.json")) {
124
			throw new RuntimeException('"config/main.json" file already present! Installation aborted.');
125
		}
126 4
		if (!file_exists("$source/DB/$db_engine.sql")) {
127
			throw new RuntimeException("Can't find system tables structure for selected database engine! Installation aborted.");
128
		}
129 4
	}
130
	/**
131
	 * @param string $source
132
	 *
133
	 * @return array[]
134
	 */
135 4
	protected static function initialize_filesystem ($source) {
136 4
		$file_index_map = json_decode(file_get_contents("$source/fs_installer.json"), true);
137 4
		require_once "$source/fs/".$file_index_map['core/thirdparty/upf.php'];
138 4
		require_once "$source/fs/".$file_index_map['core/functions.php'];
139
		// Remove default autoloader, since we have special autoloader suitable for operating inside installer where default will fail hard
140 4
		spl_autoload_unregister(array_reverse(spl_autoload_functions())[0]);
141
		/**
142
		 * Special autoloader for installer
143
		 */
144 4
		spl_autoload_register(
145 4
			function ($class) use ($file_index_map, $source) {
146 4
				$prepared_class_name = ltrim($class, '\\');
147 4
				if (strpos($prepared_class_name, 'cs\\') === 0) {
148 4
					$prepared_class_name = substr($prepared_class_name, 3);
149
				}
150 4
				$prepared_class_name = explode('\\', $prepared_class_name);
151 4
				$namespace           = count($prepared_class_name) > 1 ? implode('/', array_slice($prepared_class_name, 0, -1)) : '';
152 4
				$class_name          = array_pop($prepared_class_name);
153
				/**
154
				 * Try to load classes from different places. If not found in one place - try in another.
155
				 */
156
				if (
157 4
					strlen($file = @$file_index_map[str_replace('//', '/', "core/classes/$namespace/$class_name.php")]) ||    //Core classes
158 4
					strlen($file = @$file_index_map[str_replace('//', '/', "core/thirdparty/$namespace/$class_name.php")]) || //Third party classes
159 4
					strlen($file = @$file_index_map[str_replace('//', '/', "core/traits/$namespace/$class_name.php")]) ||     //Core traits
160 4
					strlen($file = @$file_index_map[str_replace('//', '/', "core/engines/$namespace/$class_name.php")]) ||    //Core engines
161 4
					strlen($file = @$file_index_map[str_replace('//', '/', "$namespace/$class_name.php")])                    //Classes in modules
162
				) {
163 4
					require_once "$source/fs/$file";
164 4
					return true;
165
				}
166
				return false;
167 4
			}
168
		);
169 4
		return $file_index_map;
170
	}
171
	/**
172
	 * @param array[] $file_index_map
173
	 * @param string  $source
174
	 * @param string  $target
175
	 *
176
	 * @throws RuntimeException
177
	 */
178 4
	protected static function extract ($file_index_map, $source, $target) {
179
		/**
180
		 * Extracting of engine's files
181
		 */
182 4
		foreach ($file_index_map as $file_path => $file_index) {
183 4
			$dir = dirname("$target/$file_path");
184 4
			if (!@mkdir($dir, 0770, true) && !is_dir($dir)) {
185
				throw new RuntimeException("Can't extract system files from the archive, creating directory $dir failed! Installation aborted.");
186
			}
187 4
			if (!copy("$source/fs/$file_index", "$target/$file_path")) {
188 4
				throw new RuntimeException("Can't extract system files from the archive, creating file $target/$file_path failed! Installation aborted.");
189
			}
190
		}
191 4
		file_put_json("$target/core/fs.json", array_keys(file_get_json("$source/fs.json")));
192
		/**
193
		 * Make CLI executable
194
		 */
195 4
		chmod("$target/cli", 0770);
196 4
	}
197
	/**
198
	 * @param string $target
199
	 * @param string $domain
200
	 * @param string $timezone
201
	 * @param string $db_host
202
	 * @param string $db_engine
203
	 * @param string $db_name
204
	 * @param string $db_user
205
	 * @param string $db_password
206
	 * @param string $db_prefix
207
	 * @param string $language
208
	 * @param string $public_key
209
	 *
210
	 * @throws RuntimeException
211
	 */
212 4
	protected static function initialize_core_config (
213
		$target,
214
		$domain,
215
		$timezone,
216
		$db_host,
217
		$db_engine,
218
		$db_name,
219
		$db_user,
220
		$db_password,
221
		$db_prefix,
222
		$language,
223
		$public_key
224
	) {
225 4
		$db_password = str_replace('"', '\\"', $db_password);
226 4
		$config      = str_replace(
227 4
			['@domain', '@timezone', '@db_host', '@db_type', '@db_name', '@db_user', '@db_password', '@db_prefix', '@language', '@public_key'],
228 4
			[$domain, $timezone, $db_host, $db_engine, $db_name, $db_user, $db_password, $db_prefix, $language, $public_key],
229 4
			self::MAIN_CONFIG_STUB
230
		);
231 4
		if (!file_put_contents("$target/config/main.json", $config)) {
232
			throw new RuntimeException("Can't write core system configuration! Installation aborted.");
233
		}
234 4
		chmod("$target/config/main.json", 0600);
235 4
	}
236
	/**
237
	 * @param DB\_Abstract $cdb
238
	 * @param string       $source
239
	 * @param string       $db_engine
240
	 *
241
	 * @throws RuntimeException
242
	 */
243 4
	protected static function initialize_db_structure ($cdb, $source, $db_engine) {
244 4
		$query = array_filter(
245 4
			explode(';', file_get_contents("$source/DB/$db_engine.sql")),
246 4
			'trim'
247
		);
248 4
		if (!$cdb->q($query)) {
249
			throw new RuntimeException("Can't import system tables structure for selected database engine! Installation aborted.");
250
		}
251 4
	}
252
	/**
253
	 * @param DB\_Abstract $cdb
254
	 * @param string       $source
255
	 * @param string       $site_name
256
	 * @param string       $url
257
	 * @param string       $admin_email
258
	 * @param string       $language
259
	 * @param string       $domain
260
	 * @param string       $timezone
261
	 * @param int          $mode
262
	 *
263
	 * @throws RuntimeException
264
	 */
265 4
	protected static function initialize_system_config ($cdb, $source, $site_name, $url, $admin_email, $language, $domain, $timezone, $mode) {
266
		$config     = [
267 4
			'site_name'         => $site_name,
268 4
			'url'               => [$url],
269 4
			'admin_email'       => $admin_email,
270 4
			'language'          => $language,
271 4
			'active_languages'  => [$language],
272 4
			'cookie_domain'     => [$domain],
273 4
			'timezone'          => $timezone,
274 4
			'mail_from'         => $admin_email,
275 4
			'mail_from_name'    => "Administrator of $site_name",
276 4
			'simple_admin_mode' => $mode
277
		];
278
		$components = [
279
			'modules' => [
280
				'System' => [
281 4
					'active' => Config\Module_Properties::ENABLED,
282
					'db'     => [
283
						'keys'  => 0,
284
						'users' => 0,
285
						'texts' => 0
286
					]
287
				]
288 4
			],
289
			'blocks'  => []
290
		];
291 4
		foreach (file_get_json("$source/modules.json") as $module) {
292
			$components['modules'][$module] = [
293
				'active'  => Config\Module_Properties::UNINSTALLED,
294
				'db'      => [],
295
				'storage' => []
296
			];
297
		}
298 4
		$result = $cdb->q(
299
			"INSERT INTO `[prefix]config` (
300
				`domain`, `core`, `db`, `storage`, `components`
301
			) VALUES (
302
				'%s', '%s', '[]', '[]', '%s'
303 4
			)",
304
			$domain,
305
			_json_encode($config),
306
			_json_encode($components)
307
		);
308 4
		if (!$result) {
309
			throw new RuntimeException("Can't import system configuration into database! Installation aborted.");
310
		}
311 4
	}
312
	/**
313
	 * @param DB\_Abstract $cdb
314
	 * @param string       $admin_email
315
	 * @param string       $admin_password
316
	 * @param string       $public_key
317
	 *
318
	 * @throws RuntimeException
319
	 */
320 4
	protected static function create_root_administrator ($cdb, $admin_email, $admin_password, $public_key) {
321 4
		$admin_email = strtolower($admin_email);
322 4
		$admin_login = strstr($admin_email, '@', true);
323 4
		$result      = $cdb->q(
324
			"INSERT INTO `[prefix]users` (
325
				`login`, `login_hash`, `password_hash`, `email`, `email_hash`, `reg_date`, `reg_ip`, `status`
326
			) VALUES (
327
				'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'
328 4
			)",
329
			$admin_login,
330 4
			hash('sha224', $admin_login),
331 4
			password_hash(hash('sha512', hash('sha512', $admin_password).$public_key), PASSWORD_DEFAULT),
332
			$admin_email,
333 4
			hash('sha224', $admin_email),
334
			time(),
335 4
			'127.0.0.1',
336 4
			User::STATUS_ACTIVE
337
		);
338 4
		if (!$result) {
339
			throw new RuntimeException("Can't register root administrator user! Installation aborted.");
340
		}
341 4
	}
342
}
343