Install.php 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. <?php
  2. namespace app\index\controller;
  3. use app\BaseController;
  4. use app\common\traits\JumpTrait;
  5. use think\facade\Db;
  6. use think\Request;
  7. class Install extends BaseController
  8. {
  9. use JumpTrait;
  10. public function index(Request $request)
  11. {
  12. $isInstall = false;
  13. $installPath = config_path() . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
  14. $errorInfo = null;
  15. if (is_file($installPath . 'lock' . DIRECTORY_SEPARATOR . 'install.lock')) {
  16. // 如果你已经成功安装了后台系统 并且不想再次出现安装界面,可以把下面跳转注释取消
  17. // $this->redirect('/');
  18. $isInstall = true;
  19. $errorInfo = '已安装系统,如需重新安装请删除文件:/config/install/lock/install.lock,或者删除 /install 路由';
  20. }elseif (version_compare(phpversion(), '8.1.0', '<')) {
  21. $errorInfo = 'PHP版本不能小于8.1.0';
  22. }elseif (!extension_loaded("pdo_mysql")) {
  23. $errorInfo = '当前未开启pdo_mysql,无法进行安装';
  24. }
  25. if (!is_file(root_path() . '.env')) {
  26. $errorInfo = '.env 文件不存在,请先配置 .env 文件';
  27. }
  28. if (!$request->isAjax()) {
  29. $envInfo = [
  30. 'DB_HOST' => $isInstall ? '' : env('DB_HOST', '127.0.0.1'),
  31. 'DB_NAME' => $isInstall ? '' : env('DB_NAME', 'easyadmin8'),
  32. 'DB_USER' => $isInstall ? '' : env('DB_USER', 'root'),
  33. 'DB_PASS' => $isInstall ? '' : env('DB_PASS', 'root'),
  34. 'DB_PORT' => $isInstall ? '' : env('DB_PORT', 3306),
  35. 'DB_PREFIX' => $isInstall ? '' : env('DB_PREFIX', 'ea8_'),
  36. ];
  37. $currentHost = '://';
  38. $result = compact('errorInfo', 'currentHost', 'isInstall', 'envInfo');
  39. return view('index@install/index', $result);
  40. }
  41. if ($errorInfo) $this->error($errorInfo);
  42. $charset = 'utf8mb4';
  43. $post = $request->post();
  44. $cover = $post['cover'] == 1;
  45. $database = $post['database'];
  46. $hostname = $post['hostname'];
  47. $hostport = $post['hostport'];
  48. $dbUsername = $post['db_username'];
  49. $dbPassword = $post['db_password'];
  50. $prefix = $post['prefix'];
  51. $adminUrl = $post['admin_url'];
  52. $username = $post['username'];
  53. $password = $post['password'];
  54. // 参数验证
  55. $validateError = null;
  56. // 判断是否有特殊字符
  57. $check = preg_match('/[0-9a-zA-Z]+$/', $adminUrl, $matches);
  58. if (!$check) {
  59. $validateError = '后台地址不能含有特殊字符, 只能包含字母或数字。';
  60. $this->error($validateError);
  61. }
  62. if (strlen($adminUrl) < 2) {
  63. $validateError = '后台的地址不能小于2位数';
  64. }elseif (strlen($password) < 5) {
  65. $validateError = '管理员密码不能小于5位数';
  66. }elseif (strlen($username) < 4) {
  67. $validateError = '管理员账号不能小于4位数';
  68. }
  69. if (!empty($validateError)) $this->error($validateError);
  70. $config = [
  71. "driver" => 'mysql',
  72. "host" => $hostname,
  73. "database" => $database,
  74. "port" => $hostport,
  75. "username" => $dbUsername,
  76. "password" => $dbPassword,
  77. "prefix" => $prefix,
  78. "charset" => $charset,
  79. ];
  80. // 检测数据库连接
  81. $this->checkConnect($config);
  82. // 检测数据库是否存在
  83. if (!$cover && $this->checkDatabase($database)) $this->error('数据库已存在,请选择覆盖安装或者修改数据库名');
  84. // 创建数据库
  85. $this->createDatabase($database, $config);
  86. // 导入sql语句等等
  87. $config = array_merge($config, ['database' => $database]);
  88. $this->install($username, $password, $config, $adminUrl);
  89. $this->success('系统安装成功,正在跳转登录页面');
  90. }
  91. protected function install(string $username, string $password, array $config): ?bool
  92. {
  93. $installPath = config_path() . DIRECTORY_SEPARATOR . 'install' . DIRECTORY_SEPARATOR;
  94. $sqlPath = file_get_contents($installPath . 'sql' . DIRECTORY_SEPARATOR . 'install.sql');
  95. $sqlArray = $this->parseSql($sqlPath, $config['prefix'], 'ea_');
  96. $dsn = $this->pdoDsn($config, true);
  97. try {
  98. $pdo = new \PDO($dsn, $config['username'] ?? 'root', $config['password'] ?? '');
  99. foreach ($sqlArray as $sql) {
  100. $pdo->query($sql);
  101. }
  102. $hashedPassword = password_hash($password, PASSWORD_DEFAULT);
  103. $tableName = 'system_admin';
  104. $update = [
  105. 'username' => $username,
  106. 'head_img' => '/static/admin/images/head.jpg',
  107. 'password' => $hashedPassword,
  108. 'create_time' => time(),
  109. 'update_time' => time()
  110. ];
  111. foreach ($update as $_k => $_up) {
  112. $pdo->query("UPDATE {$config['prefix']}{$tableName} SET {$_k} = '{$_up}' WHERE id = 1");
  113. }
  114. // 处理安装文件
  115. !is_dir($installPath) && @mkdir($installPath);
  116. !is_dir($installPath . 'lock' . DIRECTORY_SEPARATOR) && @mkdir($installPath . 'lock' . DIRECTORY_SEPARATOR);
  117. @file_put_contents($installPath . 'lock' . DIRECTORY_SEPARATOR . 'install.lock', date('Y-m-d H:i:s'));
  118. }catch (\Exception|\PDOException|\Throwable $e) {
  119. $this->error("系统安装失败:" . $e->getMessage());
  120. }
  121. return true;
  122. }
  123. protected function parseSql($sql = '', $to = '', $from = ''): array
  124. {
  125. list($pure_sql, $comment) = [[], false];
  126. $sql = explode("\n", trim(str_replace(["\r\n", "\r"], "\n", $sql)));
  127. foreach ($sql as $key => $line) {
  128. if ($line == '') {
  129. continue;
  130. }
  131. if (preg_match("/^(#|--)/", $line)) {
  132. continue;
  133. }
  134. if (preg_match("/^\/\*(.*?)\*\//", $line)) {
  135. continue;
  136. }
  137. if (str_starts_with($line, '/*')) {
  138. $comment = true;
  139. continue;
  140. }
  141. if (str_ends_with($line, '*/')) {
  142. $comment = false;
  143. continue;
  144. }
  145. if ($comment) {
  146. continue;
  147. }
  148. if ($from != '') {
  149. $line = str_replace('`' . $from, '`' . $to, $line);
  150. }
  151. if ($line == 'BEGIN;' || $line == 'COMMIT;') {
  152. continue;
  153. }
  154. $pure_sql[] = $line;
  155. }
  156. //$pure_sql = implode($pure_sql, "\n");
  157. $pure_sql = implode("\n", $pure_sql);
  158. return explode(";\n", $pure_sql);
  159. }
  160. protected function createDatabase($database, $config): bool
  161. {
  162. $dsn = $this->pdoDsn($config);
  163. try {
  164. $pdo = new \PDO($dsn, $config['username'] ?? 'root', $config['password'] ?? '');
  165. $pdo->query("CREATE DATABASE IF NOT EXISTS `{$database}` DEFAULT CHARACTER SET {$config['charset']} COLLATE=utf8mb4_general_ci");
  166. }catch (\PDOException $e) {
  167. return false;
  168. }
  169. return true;
  170. }
  171. protected function checkDatabase($database): bool
  172. {
  173. try {
  174. $check = Db::query("SELECT * FROM information_schema.schemata WHERE schema_name='{$database}'");
  175. }catch (\Throwable $exception) {
  176. $check = false;
  177. }
  178. if (empty($check)) {
  179. return false;
  180. }else {
  181. return true;
  182. }
  183. }
  184. protected function checkConnect(array $config): ?bool
  185. {
  186. $dsn = $this->pdoDsn($config);
  187. try {
  188. $pdo = new \PDO($dsn, $config['username'] ?? 'root', $config['password'] ?? '');
  189. $res = $pdo->query('select VERSION()');
  190. $_version = $res->fetch()[0] ?? 0;
  191. if (version_compare($_version, '5.7.0', '<')) {
  192. $this->error('mysql版本最低要求 5.7.x');
  193. }
  194. }catch (\PDOException $e) {
  195. $this->error($e->getMessage());
  196. }
  197. return true;
  198. }
  199. /**
  200. * @param array $config
  201. * @param bool $needDatabase
  202. * @return string
  203. */
  204. protected function pdoDsn(array $config, bool $needDatabase = false): string
  205. {
  206. $host = $config['host'] ?? '127.0.0.1';
  207. $database = $config['database'] ?? '';
  208. $port = $config['port'] ?? '3306';
  209. $charset = $config['charset'] ?? 'utf8mb4';
  210. if ($needDatabase) return "mysql:host=$host;port=$port;dbname=$database;charset=$charset";
  211. return "mysql:host=$host;port=$port;charset=$charset";
  212. }
  213. }