charset_type_list = [ 'utf8mb4' => [ 'charset' => 'utf8mb4', 'collate' => 'utf8mb4_general_ci', 'version' => 5.6, ], 'utf8' => [ 'charset' => 'utf8', 'collate' => 'utf8_general_ci', 'version' => 5.0, ], ]; // 安装日志上报类库 $this->behavior_obj = new \base\Behavior(); } /** * 是否已安装 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description */ private function IsInstall() { if(file_exists(ROOT.'config/database.php')) { return DataReturn('你已经安装过该系统,重新安装需要先删除 ./config/database.php 文件', -1); } return DataReturn('success', 0); } /** * 协议 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description */ public function Index() { $ret = $this->IsInstall(); if($ret['code'] != 0) { exit($ret['msg']); } $this->behavior_obj->ReportInstallLog(['msg'=>'协议阅读']); return MyView(); } /** * 检查 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description */ public function Check() { $ret = $this->IsInstall(); if($ret['code'] != 0) { exit($ret['msg']); } $this->behavior_obj->ReportInstallLog(['msg'=>'环境检测']); return MyView(); } /** * 创建数据库 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description */ public function Create() { $ret = $this->IsInstall(); if($ret['code'] != 0) { exit($ret['msg']); } $this->behavior_obj->ReportInstallLog(['msg'=>'数据信息填写']); MyViewAssign('charset_type_list' , $this->charset_type_list); return MyView(); } /** * 完成 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description */ public function Successful() { // 安装验证 SystemService::SystemInstallCheck(); // 管理入口地址 MyViewAssign('admin_run', MySession($this->admin_run_key)); return MyView(); } /** * 确认、安装应用模块创建数据库配置文件 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description */ public function Confirm() { // 是否ajax if(!IS_AJAX) { MyViewAssign('msg', '非法访问'); return MyView('public/error'); } // 参数 $params = $this->RequestParams(); $ret = $this->ParamsCheck($params); if($ret['code'] != 0) { $this->behavior_obj->ReportInstallLog(['msg'=>'参数校验['.$ret['msg'].']']); return $ret; } // 配置文件校验 $ret = $this->IsInstall(); if($ret['code'] != 0) { $this->behavior_obj->ReportInstallLog(['msg'=>'Confirm():'.$ret['msg']]); return $ret; } // 安装应用数据库配置文件生成 return $this->CreateDbConfig(APP_PATH.'install/config', $params); } /** * 安装 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description */ public function Add() { // 是否ajax if(!IS_AJAX) { MyViewAssign('msg', '非法访问'); return MyView('public/error'); } // 配置文件校验 $ret = $this->IsInstall(); if($ret['code'] != 0) { $this->behavior_obj->ReportInstallLog(['msg'=>'Add():'.$ret['msg']]); return $ret; } // 开始安装 $params = $this->RequestParams(); $db = $this->DbObj($params); if(!is_object($db)) { $this->behavior_obj->ReportInstallLog(['msg'=>'数据库连接失败']); return DataReturn('数据库连接失败', -1); } // mysql版本 $ret = $this->IsVersion($db, $params['DB_CHARSET']); if($ret['code'] != 0) { return $ret; } // 创建数据表 $ret = $this->CreateTable($db, $params); if($ret['code'] != 0) { return $ret; } // 生成数据库配置文件 $ret = $this->CreateDbConfig(ROOT.'config', $params); if($ret['code'] == 0) { $ret['msg'] = '安装成功'; $this->behavior_obj->ReportInstallLog(['msg'=>'安装成功']); } // 管理入口文件处理 return $this->AdminRunFileHandle(); } /** * 请求参数 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2023-09-23 * @desc description */ public function RequestParams() { $params = input('post.'); if(!empty($params) && is_array($params)) { // 需要去除的特殊字符 $search = ['', '\'', '"', '"', '\\', 'eval(', '&', '<', '>']; foreach($params as &$v) { // 去除的特殊字符 if(!is_array($v)) { $v = str_replace($search, '', $v); } } } return $params; } /** * 管理入口文件处理 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description * @param [array] $params [输入参数] */ private function AdminRunFileHandle($params = []) { // 处理文件名称修改 $admin_filename_new = 'admin'.strtolower(RandomString(6)).'.php'; $arr = [ROOT, ROOT.'public'.DS]; foreach($arr as $v) { if(file_exists($v.'admin.php')) { \base\FileUtil::MoveFile($v.'admin.php', $v.$admin_filename_new); } } // 记录管理地址 MySession($this->admin_run_key, __MY_URL__.$admin_filename_new.'?s=index/index.html'); return DataReturn(MyLang('operate_success'), 0); } /** * 生成配置文件 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description * @param [string] $dir [目录地址] * @param [array] $params [输入参数] */ private function CreateDbConfig($dir, $params = []) { $db_str=<< 'mysql', // 自定义时间查询规则 'time_query_rule' => [], // 自动写入时间戳字段 // true为自动识别类型 false关闭 // 字符串则明确指定时间字段类型 支持 int timestamp datetime date 'auto_timestamp' => true, // 时间字段取出后的默认时间格式 'datetime_format' => 'Y-m-d H:i:s', // 数据库连接配置信息 'connections' => [ 'mysql' => [ // 数据库类型 'type' => '{$params['DB_TYPE']}', // 服务器地址 'hostname' => '{$params['DB_HOST']}', // 数据库名 'database' => '{$params['DB_NAME']}', // 用户名 'username' => '{$params['DB_USER']}', // 密码 'password' => '{$params['DB_PWD']}', // 端口 'hostport' => '{$params['DB_PORT']}', // 数据库连接参数 'params' => [ \PDO::ATTR_CASE => \PDO::CASE_LOWER, \PDO::ATTR_EMULATE_PREPARES => true, ], // 数据库编码默认采用utf8mb4 'charset' => '{$params['DB_CHARSET']}', // 数据库表前缀 'prefix' => '{$params['DB_PREFIX']}', // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器) 'deploy' => 0, // 数据库读写是否分离 主从式有效 'rw_separate' => false, // 读写分离后 主服务器数量 'master_num' => 1, // 指定从服务器序号 'slave_no' => '', // 是否严格检查字段是否存在 'fields_strict' => true, // 是否需要断线重连 'break_reconnect' => false, // 监听SQL 'trigger_sql' => false, // 开启字段缓存 'fields_cache' => false, ] ] ]; ?> php; if(@file_put_contents($dir.'/database.php', $db_str) === false) { $this->behavior_obj->ReportInstallLog(['msg'=>'配置文件创建失败['.$dir.']']); return DataReturn('配置文件创建失败', -1); } return DataReturn(MyLang('operate_success'), 0); } /** * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description * @param [object] $db [db对象] * @param [array] $params [输入参数] */ private function CreateTable($db, $params) { if(!file_exists(ROOT.'config/shopxo.sql')) { $this->behavior_obj->ReportInstallLog(['msg'=>'数据库sql文件不存在']); return DataReturn('数据库sql文件不存在', -1); } // sql文件 $sql = file_get_contents(ROOT.'config/shopxo.sql'); // 替换表前缀 if($params['DB_PREFIX'] != 'sxo_') { $sql = str_replace("`sxo_", " `{$params['DB_PREFIX']}", $sql); } // 编码替换处理 $charset = $this->charset_type_list[$params['DB_CHARSET']]; $charset_old = $this->charset_type_list[($charset['charset'] == 'utf8') ? 'utf8mb4' : 'utf8']; // 编码替换操作 $sql = str_replace("SET NAMES {$charset_old['charset']};", "SET NAMES {$charset['charset']};", $sql); $sql = str_replace("SET {$charset_old['charset']} ", "SET {$charset['charset']} ", $sql); $sql = str_replace("COLLATE {$charset_old['collate']} ", "COLLATE {$charset['collate']} ", $sql); $sql = str_replace(["SET = {$charset_old['charset']} ", "SET={$charset_old['charset']} "], "SET={$charset['charset']} ", $sql); $sql = str_replace(["COLLATE = {$charset_old['collate']} ", "COLLATE={$charset_old['collate']} "], "COLLATE={$charset['collate']} ", $sql); $sql = str_replace(["CHARSET = {$charset_old['charset']} ", "CHARSET={$charset_old['charset']} "], "CHARSET={$charset['charset']} ", $sql); $sql = str_replace($charset_old['collate'], $charset['collate'], $sql); // 转为数组 $sql_all = preg_split("/;[\r\n]+/", $sql); $success = 0; $failure = 0; foreach($sql_all as $v) { if (!empty($v)) { if($db->execute($v) !== false) { $success++; } else { $failure++; } } } $result = [ 'success' => $success, 'failure' => $failure, ]; $this->behavior_obj->ReportInstallLog(['msg'=>'sql运行 成功['.$success.']条, 失败['.$failure.']条']); if($failure > 0) { return DataReturn('sql运行失败['.$failure.']条', -1); } // 更新管理员账号密码 $login_salt = GetNumberCode(6); $login_pwd = LoginPwdEncryption($params['ADMIN_PWD'], $login_salt); $db->execute('UPDATE `'.$params['DB_PREFIX'].'admin` SET `username`="'.$params['ADMIN_USERNAME'].'", login_pwd="'.$login_pwd.'", `login_salt`="'.$login_salt.'" WHERE `id`=1'); // 更新加密串 $db->execute('UPDATE `'.$params['DB_PREFIX'].'config` SET `value`="'.md5(time().rand(100, 99999)).'" WHERE `only_tag`="common_data_encryption_secret"'); return DataReturn('success', 0, $result); } /** * 数据库版本校验 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description * @param [object] $db [db对象] * @param [string] $db_charset [数据库编码] */ private function IsVersion($db, $db_charset) { $data = $db->query('SELECT VERSION() AS `version`'); if(empty($data[0]['version'])) { $this->behavior_obj->ReportInstallLog(['msg'=>'查询数据库版本失败']); return DataReturn('查询数据库版本失败', -1); } else { preg_match('/\d+\.\d+\.\d+/', $data[0]['version'], $matches); $mysql_version = isset($matches[0]) ? $matches[0] : null; if(empty($mysql_version)) { $this->behavior_obj->ReportInstallLog(['msg'=>'数据库版本解析失败:'.$data[0]['version']]); return DataReturn('数据库版本解析失败', -1); } if(version_compare($mysql_version, $this->charset_type_list[$db_charset]['version'], '<')) { $msg = '数据库版本过低、需要>=' . $this->charset_type_list[$db_charset]['version'] . '、当前' . $mysql_version; $this->behavior_obj->ReportInstallLog(['msg'=>$msg, 'mysql_version'=>$mysql_version]); return DataReturn($msg, -1); } } return DataReturn('success', 0); } /** * 获取数据库操作对象 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description * @param array $params [输入参数] * @param string $db_name [数据库名称] */ private function DbObj($params = [], $db_name = '') { return Db::connect('mysql'); } /** * 参数校验 * @author Devil * @blog http://gong.gg/ * @version 1.0.0 * @date 2018-12-28 * @desc description * @param [array] $params [输入参数] */ private function ParamsCheck($params = []) { // 请求类型 $p = [ [ 'checked_type' => 'empty', 'key_name' => 'DB_TYPE', 'error_msg' => '请选择数据库类型', ], [ 'checked_type' => 'in', 'key_name' => 'DB_CHARSET', 'checked_data' => array_column($this->charset_type_list, 'charset'), 'error_msg' => '请选择数据编码', ], [ 'checked_type' => 'empty', 'key_name' => 'DB_HOST', 'error_msg' => '请填写数据库服务器地址', ], [ 'checked_type' => 'empty', 'key_name' => 'DB_PORT', 'error_msg' => '请填写数据库端口', ], [ 'checked_type' => 'empty', 'key_name' => 'DB_NAME', 'error_msg' => '请填写数据库名', ], [ 'checked_type' => 'empty', 'key_name' => 'DB_USER', 'error_msg' => '请填写数据库用户名', ], [ 'checked_type' => 'empty', 'key_name' => 'DB_PWD', 'error_msg' => '请填写数据库密码', ], [ 'checked_type' => 'empty', 'key_name' => 'DB_PREFIX', 'error_msg' => '请填写数据表前缀', ], [ 'checked_type' => 'empty', 'key_name' => 'ADMIN_USERNAME', 'error_msg' => '请填写管理员账号', ], [ 'checked_type' => 'empty', 'key_name' => 'ADMIN_PWD', 'error_msg' => '请填写管理员密码', ], ]; $ret = ParamsChecked($params, $p); if($ret !== true) { return DataReturn($ret, -1); } return DataReturn('success', 0); } } ?>