1
0

Index.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. <?php
  2. namespace app\install\controller;
  3. use think\Controller;
  4. use think\Db;
  5. use think\Lang;
  6. use think\Request;
  7. class Index extends Controller
  8. {
  9. /**
  10. * 构造方法
  11. * @access public
  12. * @param Request $request Request 对象
  13. */
  14. public function __construct(Request $request = null)
  15. {
  16. // 仅安装脚本可进入
  17. if (!defined('BIND_MODULE') || BIND_MODULE != 'install') {
  18. header('HTTP/1.1 403 Forbidden');
  19. exit();
  20. }
  21. parent::__construct($request);
  22. }
  23. public function index($step = 0)
  24. {
  25. $langs = glob('./application/lang/*.php');
  26. foreach ($langs as $k => &$v) {
  27. $v = str_replace(['./application/lang/','.php'],['',''],$v);
  28. }
  29. $this->assign('langs', $langs);
  30. if(in_array(session('lang'),$langs)){
  31. $lang = Lang::range(session('lang'));
  32. Lang::load('./application/lang/'.$lang.'.php',$lang);
  33. }
  34. switch ($step) {
  35. case 2:
  36. session('install_error', false);
  37. return self::step2();
  38. break;
  39. case 3:
  40. if (session('install_error')) {
  41. return $this->error(lang('install/environment_failed'));
  42. }
  43. return self::step3();
  44. break;
  45. case 4:
  46. if (session('install_error')) {
  47. return $this->error(lang('install/environment_failed'));
  48. }
  49. return self::step4();
  50. break;
  51. case 5:
  52. if (session('install_error')) {
  53. return $this->error(lang('install/init_err'));
  54. }
  55. return self::step5();
  56. break;
  57. default:
  58. $param = input();
  59. if(!in_array($param['lang'],$langs)) {
  60. $param['lang'] = 'zh-cn';
  61. }
  62. $lang = Lang::range($param['lang']);
  63. Lang::load('./application/lang/'.$lang.'.php',$lang);
  64. session('lang',$param['lang']);
  65. $this->assign('lang',$param['lang']);
  66. session('install_error', false);
  67. return $this->fetch('install@/index/index');
  68. break;
  69. }
  70. }
  71. /**
  72. * 第二步:环境检测
  73. * @return mixed
  74. */
  75. private function step2()
  76. {
  77. $data = [];
  78. $data['env'] = self::checkNnv();
  79. $data['dir'] = self::checkDir();
  80. $data['func'] = self::checkFunc();
  81. $this->assign('data', $data);
  82. return $this->fetch('install@index/step2');
  83. }
  84. /**
  85. * 第三步:初始化配置
  86. * @return mixed
  87. */
  88. private function step3()
  89. {
  90. $install_dir = $_SERVER["SCRIPT_NAME"];
  91. $install_dir = mac_substring($install_dir, strripos($install_dir, "/")+1);
  92. $this->assign('install_dir',$install_dir);
  93. return $this->fetch('install@index/step3');
  94. }
  95. /**
  96. * 第四步:执行安装
  97. * @return mixed
  98. */
  99. private function step4()
  100. {
  101. if ($this->request->isPost()) {
  102. if (!is_writable(APP_PATH.'database.php')) {
  103. return $this->error('[app/database.php]'.lang('install/write_read_err'));
  104. }
  105. $data = input('post.');
  106. $data['type'] = 'mysql';
  107. $rule = [
  108. 'hostname|'.lang('install/server_address') => 'require',
  109. 'hostport|'.lang('install/database_port') => 'require|number',
  110. 'database|'.lang('install/database_name') => 'require',
  111. 'username|'.lang('install/database_username') => 'require',
  112. 'prefix|'.lang('install/database_pre') => 'require|regex:^[a-z0-9]{1,20}[_]{1}',
  113. 'cover|'.lang('install/overwrite_database') => 'require|in:0,1',
  114. ];
  115. $validate = $this->validate($data, $rule);
  116. if (true !== $validate) {
  117. return $this->error($validate);
  118. }
  119. $cover = $data['cover'];
  120. unset($data['cover']);
  121. $config = include APP_PATH.'database.php';
  122. foreach ($data as $k => $v) {
  123. if (array_key_exists($k, $config) === false) {
  124. return $this->error(lang('param').''.$k.''.lang('install/not_found'));
  125. }
  126. }
  127. // 不存在的数据库会导致连接失败
  128. $database = $data['database'];
  129. unset($data['database']);
  130. // 创建数据库连接
  131. $db_connect = Db::connect($data);
  132. // 检测数据库连接
  133. try{
  134. $db_connect->execute('select version()');
  135. }catch(\Exception $e){
  136. $this->error(lang('install/database_connect_err'));
  137. }
  138. // 生成数据库配置文件
  139. $data['database'] = $database;
  140. self::mkDatabase($data);
  141. // 不覆盖检测是否已存在数据库
  142. if (!$cover) {
  143. $check = $db_connect->execute('SELECT * FROM information_schema.schemata WHERE schema_name="'.$database.'"');
  144. if ($check) {
  145. $this->success(lang('install/database_name_haved'),'');
  146. }
  147. }
  148. // 创建数据库
  149. if (!$db_connect->execute("CREATE DATABASE IF NOT EXISTS `{$database}` DEFAULT CHARACTER SET utf8")) {
  150. return $this->error($db_connect->getError());
  151. }
  152. return $this->success(lang('install/database_connect_ok'), '');
  153. } else {
  154. return $this->error(lang('install/access_denied'));
  155. }
  156. }
  157. /**
  158. * 第五步:数据库安装
  159. * @return mixed
  160. */
  161. private function step5()
  162. {
  163. $account = input('post.account');
  164. $password = input('post.password');
  165. $install_dir = input('post.install_dir');
  166. $initdata = input('post.initdata');
  167. $config = include APP_PATH.'database.php';
  168. if (empty($config['hostname']) || empty($config['database']) || empty($config['username'])) {
  169. return $this->error(lang('install/please_test_connect'));
  170. }
  171. if (empty($account) || empty($password)) {
  172. return $this->error(lang('install/please_input_admin_name_pass'));
  173. }
  174. $rule = [
  175. 'account|'.lang('install/admin_name') => 'require|alphaNum',
  176. 'password|'.lang('install/admin_pass') => 'require|length:6,20',
  177. ];
  178. $validate = $this->validate(['account' => $account, 'password' => $password], $rule);
  179. if (true !== $validate) {
  180. return $this->error($validate);
  181. }
  182. if(empty($install_dir)) {
  183. $install_dir='/';
  184. }
  185. $config_new = config('maccms');
  186. $cofnig_new['app']['cache_flag'] = substr(md5(time()),0,10);
  187. $cofnig_new['app']['lang'] = session('lang');
  188. $config_new['api']['vod']['status'] = 0;
  189. $config_new['api']['art']['status'] = 0;
  190. $config_new['interface']['status'] = 0;
  191. $config_new['interface']['pass'] = mac_get_rndstr(16);
  192. $config_new['site']['install_dir'] = $install_dir;
  193. // 更新程序配置文件
  194. $res = mac_arr2file(APP_PATH . 'extra/maccms.php', $config_new);
  195. if ($res === false) {
  196. return $this->error(lang('write_err_config'));
  197. }
  198. // 导入系统初始数据库结构
  199. // 导入SQL
  200. $sql_file = APP_PATH.'install/sql/install.sql';
  201. if (file_exists($sql_file)) {
  202. $sql = file_get_contents($sql_file);
  203. $sql_list = mac_parse_sql($sql, 0, ['mac_' => $config['prefix']]);
  204. if ($sql_list) {
  205. $sql_list = array_filter($sql_list);
  206. foreach ($sql_list as $v) {
  207. try {
  208. Db::execute($v);
  209. } catch(\Exception $e) {
  210. return $this->error(lang('install/sql_err'). $e);
  211. }
  212. }
  213. }
  214. }
  215. //初始化数据
  216. if($initdata=='1'){
  217. $sql_file = APP_PATH.'install/sql/initdata.sql';
  218. if (file_exists($sql_file)) {
  219. $sql = file_get_contents($sql_file);
  220. $sql_list = mac_parse_sql($sql, 0, ['mac_' => $config['prefix']]);
  221. if ($sql_list) {
  222. $sql_list = array_filter($sql_list);
  223. foreach ($sql_list as $v) {
  224. try {
  225. Db::execute($v);
  226. } catch(\Exception $e) {
  227. return $this->error(lang('install/init_data_err'). $e);
  228. }
  229. }
  230. }
  231. }
  232. }
  233. // 注册管理员账号
  234. $data = [
  235. 'admin_name' => $account,
  236. 'admin_pwd' => $password,
  237. 'admin_status' =>1,
  238. ];
  239. $res = model('Admin')->saveData($data);
  240. if (!$res['code']>1) {
  241. return $this->error(lang('install/admin_name_err').':'.$res['msg']);
  242. }
  243. file_put_contents(APP_PATH.'data/install/install.lock', date('Y-m-d H:i:s'));
  244. // 获取站点根目录
  245. $root_dir = request()->baseFile();
  246. $root_dir = preg_replace(['/install.php$/'], [''], $root_dir);
  247. return $this->success(lang('install/is_ok'), $root_dir.'admin.php');
  248. }
  249. /**
  250. * 环境检测
  251. * @return array
  252. */
  253. private function checkNnv()
  254. {
  255. $items = [
  256. 'os' => [lang('install/os'), lang('install/not_limited'), 'Windows/Unix', PHP_OS, 'ok'],
  257. 'php' => [lang('install/php'), '5.5', '5.5及以上', PHP_VERSION, 'ok'],
  258. ];
  259. if ($items['php'][3] < $items['php'][1]) {
  260. $items['php'][4] = 'no';
  261. session('install_error', true);
  262. }
  263. /*
  264. $tmp = function_exists('gd_info') ? gd_info() : [];
  265. if (empty($tmp['GD Version'])) {
  266. $items['gd'][3] = lang('install/not_installed');
  267. $items['gd'][4] = 'no';
  268. session('install_error', true);
  269. } else {
  270. $items['gd'][3] = $tmp['GD Version'];
  271. }
  272. */
  273. return $items;
  274. }
  275. /**
  276. * 目录权限检查
  277. * @return array
  278. */
  279. private function checkDir()
  280. {
  281. $items = [
  282. ['file', './application/database.php', lang('install/read_and_write'), lang('install/read_and_write'), 'ok'],
  283. ['file', './application/route.php', lang('install/read_and_write'), lang('install/read_and_write'), 'ok'],
  284. ['dir', './application/extra', lang('install/read_and_write'), lang('install/read_and_write'), 'ok'],
  285. ['dir', './application/data/backup', lang('install/read_and_write'), lang('install/read_and_write'), 'ok'],
  286. ['dir', './application/data/update', lang('install/read_and_write'), lang('install/read_and_write'), 'ok'],
  287. ['dir', './runtime', lang('install/read_and_write'), lang('install/read_and_write'), 'ok'],
  288. ['dir', './upload', lang('install/read_and_write'), lang('install/read_and_write'), 'ok'],
  289. ];
  290. foreach ($items as &$v) {
  291. if ($v[0] == 'dir') {// 文件夹
  292. if(!is_writable($v[1])) {
  293. if(is_dir($v[1])) {
  294. $v[3] = lang('install/not_writable');
  295. $v[4] = 'no';
  296. } else {
  297. $v[3] = lang('install/not_found');
  298. $v[4] = 'no';
  299. }
  300. session('install_error', true);
  301. }
  302. } else {// 文件
  303. if(!is_writable($v[1])) {
  304. $v[3] = lang('install/not_writable');
  305. $v[4] = 'no';
  306. session('install_error', true);
  307. }
  308. }
  309. }
  310. return $items;
  311. }
  312. /**
  313. * 函数及扩展检查
  314. * @return array
  315. */
  316. private function checkFunc()
  317. {
  318. $items = [
  319. ['pdo', lang('install/support'), 'yes',lang('install/class')],
  320. ['pdo_mysql', lang('install/support'), 'yes', lang('install/model')],
  321. ['zip', lang('install/support'), 'yes', lang('install/model')],
  322. ['fileinfo', lang('install/support'), 'yes', lang('install/model')],
  323. ['curl', lang('install/support'), 'yes', lang('install/model')],
  324. ['xml', lang('install/support'), 'yes', lang('install/function')],
  325. ['file_get_contents', lang('install/support'), 'yes', lang('install/function')],
  326. ['mb_strlen', lang('install/support'), 'yes', lang('install/function')],
  327. ];
  328. if(version_compare(PHP_VERSION,'5.6.0','ge') && version_compare(PHP_VERSION,'5.7.0','lt')){
  329. $items[] = ['always_populate_raw_post_data',lang('install/support'),'yes',lang('install/config')];
  330. }
  331. foreach ($items as &$v) {
  332. if(('类'==$v[3] && !class_exists($v[0])) || (lang('install/model')==$v[3] && !extension_loaded($v[0])) || (lang('install/function')==$v[3] && !function_exists($v[0])) || (lang('install/config')==$v[3] && ini_get('always_populate_raw_post_data')!=-1)) {
  333. $v[1] = lang('install/not_support');
  334. $v[2] = 'no';
  335. session('install_error', true);
  336. }
  337. }
  338. return $items;
  339. }
  340. /**
  341. * 生成数据库配置文件
  342. * @return array
  343. */
  344. private function mkDatabase(array $data)
  345. {
  346. $code = <<<INFO
  347. <?php
  348. // +----------------------------------------------------------------------
  349. // | ThinkPHP [ WE CAN DO IT JUST THINK ]
  350. // +----------------------------------------------------------------------
  351. // | Copyright (c) 2006~2016 http://thinkphp.cn All rights reserved.
  352. // +----------------------------------------------------------------------
  353. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  354. // +----------------------------------------------------------------------
  355. // | Author: liu21st <[email protected]>
  356. // +----------------------------------------------------------------------
  357. return [
  358. // 数据库类型
  359. 'type' => 'mysql',
  360. // 服务器地址
  361. 'hostname' => '{$data['hostname']}',
  362. // 数据库名
  363. 'database' => '{$data['database']}',
  364. // 用户名
  365. 'username' => '{$data['username']}',
  366. // 密码
  367. 'password' => '{$data['password']}',
  368. // 端口
  369. 'hostport' => '{$data['hostport']}',
  370. // 连接dsn
  371. 'dsn' => '',
  372. // 数据库连接参数
  373. 'params' => [],
  374. // 数据库编码默认采用utf8
  375. 'charset' => 'utf8',
  376. // 数据库表前缀
  377. 'prefix' => '{$data['prefix']}',
  378. // 数据库调试模式
  379. 'debug' => false,
  380. // 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
  381. 'deploy' => 0,
  382. // 数据库读写是否分离 主从式有效
  383. 'rw_separate' => false,
  384. // 读写分离后 主服务器数量
  385. 'master_num' => 1,
  386. // 指定从服务器序号
  387. 'slave_no' => '',
  388. // 是否严格检查字段是否存在
  389. 'fields_strict' => false,
  390. // 数据集返回类型
  391. 'resultset_type' => 'array',
  392. // 自动写入时间戳字段
  393. 'auto_timestamp' => false,
  394. // 时间字段取出后的默认时间格式
  395. 'datetime_format' => 'Y-m-d H:i:s',
  396. // 是否需要进行SQL性能分析
  397. 'sql_explain' => false,
  398. // Builder类
  399. 'builder' => '',
  400. // Query类
  401. 'query' => '\\think\\db\\Query',
  402. ];
  403. INFO;
  404. file_put_contents(APP_PATH.'database.php', $code);
  405. // 判断写入是否成功
  406. $config = include APP_PATH.'database.php';
  407. if (empty($config['database']) || $config['database'] != $data['database']) {
  408. return $this->error('[application/database.php]'.lang('write_err_database'));
  409. exit;
  410. }
  411. }
  412. }