Completed
Push — master ( bf102e...fafcea )
by Nazar
06:46
created

Installer::create_root_administrator()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 22
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 2.0009

Importance

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