// +---------------------------------------------------------------------- namespace app\install\controller; use app\BaseController; use app\install\validate\Database; use mall\utils\Tool; use think\facade\Cache; use think\facade\Db; use think\facade\Request; use think\facade\Session; use think\facade\View; class Index extends BaseController { protected $path = ''; protected function initialize(){ $this->path = dirname(dirname(__FILE__)) . '/data'; if (file_exists( $this->path.'/install.lock')) { exit('程序已安装,如果需要重新安装,请删除 install 模块下 data 目录下的 install.lock 文件'); } } public function index(){ //Session::set("step",1); return View::fetch(); } public function check(){ Session::set("step",2); Session::set("error",false); // 环境检测 $env = $this->check_env(); // 目录文件读写检测 $dirFile = $this->check_dirfile(); // 函数检测 $func = $this->check_func(); return View::fetch("",[ "env"=>$env, "dirFile"=>$dirFile, "func"=>$func, "isNext"=>false == Session::get('error') ]); } public function config(){ if(Request::isAjax()){ $data = Request::param(); try { validate(Database::class)->check($data); }catch (\Exception $ex){ return json(["status"=>0,"msg"=>$ex->getMessage()]); } // 缓存配置数据 $data['type'] = 'mysql'; Session::set('installData', $data); try { $mysqli = mysqli_connect( $data['hostname'], $data['username'], $data['password'] ); if(!$mysqli){ throw new \Exception("连接数据库失败,请检查配置是否正确。"); } // 检测数据库连接并检测版本 $r = mysqli_query($mysqli,'select version() as version limit 1;'); $version = mysqli_fetch_assoc($r); if (version_compare($version['version'], '5.5.3', '<')) { throw new \Exception('数据库版本过低,必须 5.5.3 及以上'); } // 检测是否已存在数据库 $sql = 'SELECT count(*) as count FROM information_schema.schemata WHERE schema_name="'.$data['database'].'"'; $result = mysqli_query($mysqli,$sql); $row = mysqli_fetch_assoc($result); if ($row["count"] <= 0) { // 创建数据库 $sql = "CREATE DATABASE IF NOT EXISTS `{$data['database']}` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;"; if (!mysqli_query($mysqli,$sql)) { throw new \Exception("新建数据库失败,请手动建立数据库。"); } } mysqli_close($mysqli); } catch (\Exception $e) { return json(["status"=>0,"msg"=>$e->getMessage()]); } // 准备工作完成 return json(["status"=>1,"msg"=>"ok","url"=>str_replace('/install/','/',createUrl('index/install'))]); } if (Session::get('step') != 2) { $this->redirect("check"); } Cache::clear(); Session::set("step",3); return View::fetch(); } public function install(){ if(Request::isAjax()){ // 数据准备 $data = Session::get('installData'); $type = Request::param('type'); $result = ['code' => 1, "data"=>['type' => $type]]; if (!$type) { $result['data']['type'] = 'database'; $result['data']['status'] = 1; $result['msg'] = '开始安装数据库表'; $result['url'] = str_replace('/install/','/',createUrl('index/install')); return json($result); } // 连接数据库 $db = null; if (in_array($type, ['database', 'config'])) { try { $db = mysqli_connect($data['hostname'],$data['username'],$data['password'],$data['database'],$data['hostport']); } catch (\Exception $e) { $result['code'] = 0; $result['msg'] = $e->getMessage(); return json($result); } $db->set_charset('utf8mb4'); } if ('database' == $type) { $database = Cache::remember('database', function () { $data = Session::get('installData'); $sql = file_get_contents($this->path . '/a3mall' . ($data['is_demo'] == 1 ? '_demo' : '') . '.sql'); //$sql = str_replace("`mall_","`".$data["prefix"],$sql); $sql = str_replace("\r", "\n", $sql); $sql = explode(";\n", $sql); Cache::tag('install', 'database'); return $sql; }); // 数据库表安装完成 $msg = ''; $idx = Request::param('idx','0','intval'); if ($idx >= count($database)) { $result['data']['type'] = 'config'; $result['data']['status'] = 1; $result['msg'] = '开始安装配置文件'; $result['url'] = str_replace('/install/','/',createUrl('index/install')); return json($result); } // 插入数据库表 if (array_key_exists($idx, $database)) { $sql = $value = trim($database[$idx]); if (!empty($value)) { try { if (false !== mysqli_query($db,$sql)) { $msg = $this->get_sql_message($sql); } else { throw new \Exception(mysqli_error($db)); } } catch (\Exception $e) { $result['code'] = 0; $result['msg'] = $e->getMessage(); return json($result); } } } // 返回下一步 $result['data']['status'] = 1; $result['msg'] = $msg; $result['url'] = str_replace('/install/','/',createUrl('index/install',['idx'=>$idx+1])); return json($result); } // 安装配置文件 if ('config' == $type) { // 创建数据库配置文件 $domain = trim(Request::domain(),"/") . "/"; $string = <<getRandString(35)} SIGN = {$this->getRandString(10)} EOF; if (!file_put_contents(Tool::getRootPath() . '.env', $string)) { $result['code'] = 0; $result['msg'] = "数据库配置文件写入失败。"; return json($result); } // 创建超级管理员 $adminData = [ 'id' => 1, 'role_id' => 1, 'username' => $data["admin_user"], 'password' => md5($data['admin_password']), 'lock' => 1, 'status' => 0, 'count' => 1, 'time' => time() ]; try { $u = mysqli_query($db,"select * from {$data['prefix']}system_users where id=1"); $users = mysqli_fetch_assoc($u); if(!empty($users)){ $sql = "UPDATE `{$data['prefix']}system_users` SET "; unset($adminData['id']); foreach($adminData as $k=>$v){ $sql .= '`'.$k.'` = ' . "'" . $v . "',"; } $sql = trim($sql,','); $sql .= " WHERE `id` = 1;"; mysqli_query($db,$sql); }else{ $sql = "INSERT INTO `{$data['prefix']}system_users` (".implode(",",array_keys($adminData)).") VALUES (".("'".implode("','",array_values($adminData)) . "'").")"; mysqli_query($db,$sql); } } catch (\Exception $e) { $result['code'] = 0; $result['msg'] = $e->getMessage(); return json($result); } $result['data']['status'] = 0; $result['msg'] = '安装完成!'; $result['url'] = str_replace('/install/','/',createUrl('index/complete')); return json($result); } $result['code'] = 0; $result['msg'] = "安装出现错误,未完成安装。"; return json($result); } if (Session::get('step') != 3) { $this->redirect("config"); } Session::set('step', 4); return View::fetch(); } public function complete(){ Cache::clear(); file_put_contents($this->path . '/install.lock', ''); return View::fetch("",[ "data"=>Session::get('installData') ]); } private function check_env(){ $items = [ 'os' => ['操作系统', '不限制', '类Unix', PHP_OS, 'check'], 'php' => ['PHP版本', '7.1', '7.2+', PHP_VERSION, 'check'], 'upload' => ['附件上传', '不限制', '2M+', '未知', 'check'], 'gd' => ['GD库', '2.0', '2.0+', '未知', 'check'], 'disk' => ['磁盘空间', '100M', '不限制', '未知', 'check'], ]; // PHP环境检测 if ($items['php'][3] < $items['php'][1]) { $items['php'][4] = 'error'; Session::set('error', true); } // 附件上传检测 if (@ini_get('file_uploads')) $items['upload'][3] = ini_get('upload_max_filesize'); // GD库检测 $tmp = function_exists('gd_info') ? gd_info() : []; if (empty($tmp['GD Version'])) { $items['gd'][3] = '未安装'; $items['gd'][4] = 'error'; Session::set('error', true); } else { $items['gd'][3] = $tmp['GD Version']; } unset($tmp); // 磁盘空间检测 if (function_exists('disk_free_space')) { $disk_size = floor(disk_free_space(Tool::getRootPath()) / (1024 * 1024)); $items['disk'][3] = $disk_size . 'M'; if ($disk_size < 100) { $items['disk'][4] = 'error'; Session::set('error', true); } } return $items; } private function check_dirfile(){ $items = [ ['file', '可写', 'check', '.env'], ['dir', '可写', 'check', 'app'], ['dir', '可写', 'check', 'runtime'], ['dir', '可写', 'check', 'mall'], ['dir', '可写', 'check', 'public/static'], ['dir', '可写', 'check', 'public/uploads'], ]; foreach ($items as &$val) { $item = Tool::getRootPath() . $val[3]; if ('dir' == $val[0]) { if (!is_writable($item)) { if (is_dir($item)) { $val[1] = '可读'; $val[2] = 'error'; Session::set('error', true); } else { $val[1] = '不存在'; $val[2] = 'error'; Session::set('error', true); } } } else { if (file_exists($item)) { if (!is_writable($item)) { $val[1] = '不可写'; $val[2] = 'error'; Session::set('error', true); } } else { if (!is_writable(dirname($item))) { $val[1] = '不存在'; $val[2] = 'error'; Session::set('error', true); } } } } return $items; } private function check_func(){ $items = [ ['pdo', '支持', 'check', '类'], ['pdo_mysql', '支持', 'check', '模块'], ['openssl', '支持', 'check', '模块'], ['curl', '支持', 'check', '模块'], ['bcmath', '支持', 'check', '模块'], ['mbstring', '支持', 'check', '模块'], ['scandir', '支持', 'check', '函数'], ['file_get_contents', '支持', 'check', '函数'], ['version_compare', '支持', 'check', '函数'], ]; foreach ($items as &$val) { if (('类' == $val[3] && !class_exists($val[0])) || ('模块' == $val[3] && !extension_loaded($val[0])) || ('函数' == $val[3] && !function_exists($val[0])) ) { $val[1] = '不支持'; $val[2] = 'error'; Session::set('error', true); } } return $items; } private function get_sql_message($sql){ if (preg_match('/CREATE TABLE? `([^ ]*)`/is', $sql, $matches)) { return !empty($matches[1]) ? "创建数据库表 {$matches[1]} 完成" : ''; } if (preg_match('/INSERT INTO? `([^ ]*)`/is', $sql, $matches)) { return !empty($matches[1]) ? "插入数据库行 {$matches[1]} 完成" : ''; } if (preg_match('/ALTER TABLE? `([^ ]*)`/is', $sql, $matches)) { if (mb_strpos($sql, 'MODIFY')) { return !empty($matches[1]) ? "调整数据库索引 {$matches[1]} 完成" : ''; } else { return !empty($matches[1]) ? "创建数据库索引 {$matches[1]} 完成" : ''; } } return ''; } private function getRandString($len=10){ $string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; $length = strlen($string)-1; $s = ''; for ($i=0;$i<$len;$i++) { $num=mt_rand(0,$length); $s .= $string[$num]; } return $s; } }