nazar-pc /
CleverStyle-Framework
| 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
Bug
introduced
by
Loading history...
|
|||||
| 302 | 5 | _json_encode($config), |
|||
| 303 | 5 | _json_encode($components) |
|||
| 304 | ); |
||||
| 305 | 5 | if (!$result) { |
|||
|
0 ignored issues
–
show
|
|||||
| 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
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
Loading history...
|
|||||
| 332 | 5 | '127.0.0.1', |
|||
| 333 | 5 | User::STATUS_ACTIVE |
|||
| 334 | ); |
||||
| 335 | 5 | if (!$result) { |
|||
|
0 ignored issues
–
show
|
|||||
| 336 | throw new RuntimeException("Can't register root administrator user! Installation aborted."); |
||||
| 337 | } |
||||
| 338 | 5 | } |
|||
| 339 | } |
||||
| 340 |