Issues (1358)

install/Installer.php (5 issues)

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

301
			/** @scrutinizer ignore-type */ $domain,
Loading history...
302 5
			_json_encode($config),
303 5
			_json_encode($components)
304
		);
305 5
		if (!$result) {
0 ignored issues
show
The condition $result is always false.
Loading history...
306
			throw new RuntimeException("Can't import system configuration into database! Installation aborted.");
307
		}
308 5
	}
309
	/**
310
	 * @param DB\_Abstract $cdb
311
	 * @param string       $admin_email
312
	 * @param string       $admin_password
313
	 * @param string       $public_key
314
	 *
315
	 * @throws RuntimeException
316
	 */
317 5
	protected static function create_root_administrator ($cdb, $admin_email, $admin_password, $public_key) {
318 5
		$admin_email = strtolower($admin_email);
319 5
		$admin_login = strstr($admin_email, '@', true);
320 5
		$result      = $cdb->q(
321 5
			"INSERT INTO `[prefix]users` (
322
				`login`, `login_hash`, `password_hash`, `email`, `email_hash`, `reg_date`, `reg_ip`, `status`
323
			) VALUES (
324
				'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'
325
			)",
326 5
			$admin_login,
0 ignored issues
show
$admin_login of type string is incompatible with the type array expected by parameter $parameters of cs\DB\_Abstract::q(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

326
			/** @scrutinizer ignore-type */ $admin_login,
Loading history...
327 5
			hash('sha224', $admin_login),
328 5
			password_hash(hash('sha512', hash('sha512', $admin_password).$public_key), PASSWORD_DEFAULT),
329 5
			$admin_email,
330 5
			hash('sha224', $admin_email),
331 5
			time(),
0 ignored issues
show
time() of type integer is incompatible with the type array expected by parameter $parameters of cs\DB\_Abstract::q(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

331
			/** @scrutinizer ignore-type */ time(),
Loading history...
332 5
			'127.0.0.1',
333 5
			User::STATUS_ACTIVE
334
		);
335 5
		if (!$result) {
0 ignored issues
show
The condition $result is always false.
Loading history...
336
			throw new RuntimeException("Can't register root administrator user! Installation aborted.");
337
		}
338 5
	}
339
}
340