Database.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <?php
  2. namespace app\admin\controller;
  3. use think\Db;
  4. use app\common\util\Dir;
  5. use app\common\util\Database as dbOper;
  6. class Database extends Base
  7. {
  8. var $_db_config;
  9. public function __construct()
  10. {
  11. parent::__construct();
  12. }
  13. public function index()
  14. {
  15. $group = input('group');
  16. if($group=='import'){
  17. //列出备份文件列表
  18. $path = trim( $GLOBALS['config']['db']['backup_path'], '/').DS;
  19. if (!is_dir($path)) {
  20. Dir::create($path);
  21. }
  22. $flag = \FilesystemIterator::KEY_AS_FILENAME;
  23. $glob = new \FilesystemIterator($path, $flag);
  24. $list = [];
  25. foreach ($glob as $name => $file) {
  26. if(preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql(?:\.gz)?$/', $name)){
  27. $name = sscanf($name, '%4s%2s%2s-%2s%2s%2s-%d');
  28. $date = "{$name[0]}-{$name[1]}-{$name[2]}";
  29. $time = "{$name[3]}:{$name[4]}:{$name[5]}";
  30. $part = $name[6];
  31. if(isset($list["{$date} {$time}"])){
  32. $info = $list["{$date} {$time}"];
  33. $info['part'] = max($info['part'], $part);
  34. $info['size'] = $info['size'] + $file->getSize();
  35. } else {
  36. $info['part'] = $part;
  37. $info['size'] = $file->getSize();
  38. }
  39. $extension = strtoupper($file->getExtension());
  40. $info['compress'] = ($extension === 'SQL') ? '无' : $extension;
  41. $info['time'] = strtotime("{$date} {$time}");
  42. $list["{$date} {$time}"] = $info;
  43. }
  44. }
  45. }
  46. else{
  47. $group='export';
  48. $list = Db::query("SHOW TABLE STATUS");
  49. }
  50. $this->assign('list',$list);
  51. $this->assign('title',lang('admin/database/title'));
  52. return $this->fetch('admin@database/'.$group);
  53. }
  54. public function export($ids = '', $start = 0)
  55. {
  56. if ($this->request->isPost()) {
  57. if (empty($ids)) {
  58. return $this->error(lang('admin/database/select_export_table'));
  59. }
  60. if (!is_array($ids)) {
  61. $tables[] = $ids;
  62. } else {
  63. $tables = $ids;
  64. }
  65. $have_admin = false;
  66. $admin_table='';
  67. foreach($tables as $k=>$v){
  68. if(strpos($v,'_admin')!==false){
  69. $have_admin=true;
  70. $admin_table = $v;
  71. unset($tables[$k]);
  72. }
  73. }
  74. if($have_admin){
  75. $tables[] = $admin_table;
  76. }
  77. //读取备份配置
  78. $config = array(
  79. 'path' => $GLOBALS['config']['db']['backup_path'] .DS,
  80. 'part' => $GLOBALS['config']['db']['part_size'] ,
  81. 'compress' => $GLOBALS['config']['db']['compress'] ,
  82. 'level' => $GLOBALS['config']['db']['compress_level'] ,
  83. );
  84. //检查是否有正在执行的任务
  85. $lock = "{$config['path']}backup.lock";
  86. if(is_file($lock)){
  87. return $this->error(lang('admin/database/lock_check'));
  88. } else {
  89. if (!is_dir($config['path'])) {
  90. Dir::create($config['path'], 0755, true);
  91. }
  92. //创建锁文件
  93. file_put_contents($lock, $this->request->time());
  94. }
  95. //生成备份文件信息
  96. $file = [
  97. 'name' => date('Ymd-His', $this->request->time()),
  98. 'part' => 1,
  99. ];
  100. // 创建备份文件
  101. $database = new dbOper($file, $config);
  102. if($database->create() !== false) {
  103. // 备份指定表
  104. foreach ($tables as $table) {
  105. $start = $database->backup($table, $start);
  106. while (0 !== $start) {
  107. if (false === $start) {
  108. return $this->error(lang('admin/database/backup_err'));
  109. }
  110. $start = $database->backup($table, $start[0]);
  111. }
  112. }
  113. // 备份完成,删除锁定文件
  114. unlink($lock);
  115. }
  116. return $this->success(lang('admin/database/backup_ok'));
  117. }
  118. return $this->error(lang('admin/database/backup_err'));
  119. }
  120. /**
  121. * 恢复数据库 [参考原作者 麦当苗儿 <[email protected]>]
  122. * @param string|array $ids 表名
  123. * @param integer $start 起始行数
  124. * @author 橘子俊 <[email protected]>
  125. * @return mixed
  126. */
  127. public function import($id = '')
  128. {
  129. if (empty($id)) {
  130. return $this->error(lang('admin/database/select_file'));
  131. }
  132. $name = date('Ymd-His', $id) . '-*.sql*';
  133. $path = trim( $GLOBALS['config']['db']['backup_path'] , '/').DS.$name;
  134. $files = glob($path);
  135. $list = array();
  136. foreach($files as $name){
  137. $basename = basename($name);
  138. $match = sscanf($basename, '%4s%2s%2s-%2s%2s%2s-%d');
  139. $gz = preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql.gz$/', $basename);
  140. $list[$match[6]] = array($match[6], $name, $gz);
  141. }
  142. ksort($list);
  143. // 检测文件正确性
  144. $last = end($list);
  145. if(count($list) === $last[0]){
  146. foreach ($list as $item) {
  147. $config = [
  148. 'path' => trim($GLOBALS['config']['db']['backup_path'], '/').DS,
  149. 'compress' => $item[2]
  150. ];
  151. $database = new dbOper($item, $config);
  152. $start = $database->import(0);
  153. // 导入所有数据
  154. while (0 !== $start) {
  155. if (false === $start) {
  156. return $this->error(lang('admin/database/import_err'));
  157. }
  158. $start = $database->import($start[0]);
  159. }
  160. }
  161. return $this->success(lang('admin/database/import_ok'));
  162. }
  163. return $this->error(lang('admin/database/file_damage'));
  164. }
  165. public function optimize($ids = '')
  166. {
  167. if (empty($ids)) {
  168. return $this->error(lang('admin/database/select_optimize_table'));
  169. }
  170. if (!is_array($ids)) {
  171. $table[] = $ids;
  172. } else {
  173. $table = $ids;
  174. }
  175. $tables = implode('`,`', $table);
  176. $res = Db::query("OPTIMIZE TABLE `{$tables}`");
  177. if ($res) {
  178. return $this->success(lang('admin/database/optimize_ok'));
  179. }
  180. return $this->error(lang('admin/database/optimize_err'));
  181. }
  182. public function repair($ids = '')
  183. {
  184. if (empty($ids)) {
  185. return $this->error(lang('admin/database/select_repair_table'));
  186. }
  187. if (!is_array($ids)) {
  188. $table[] = $ids;
  189. } else {
  190. $table = $ids;
  191. }
  192. $tables = implode('`,`', $table);
  193. $res = Db::query("REPAIR TABLE `{$tables}`");
  194. if ($res) {
  195. return $this->success(lang('admin/database/repair_ok'));
  196. }
  197. return $this->error(lang('admin/database/repair_ok'));
  198. }
  199. public function del($id = '')
  200. {
  201. if (empty($id)) {
  202. return $this->error(lang('admin/database/select_del_file'));
  203. }
  204. $name = date('Ymd-His', $id) . '-*.sql*';
  205. $path = trim($GLOBALS['config']['db']['backup_path']).DS.$name;
  206. array_map("unlink", glob($path));
  207. if(count(glob($path)) && glob($path)){
  208. return $this->error(lang('del_err'));
  209. }
  210. return $this->success(lang('del_ok'));
  211. }
  212. public function sql()
  213. {
  214. if($this->request->isPost()){
  215. $param=input();
  216. $validate = \think\Loader::validate('Token');
  217. if(!$validate->check($param)){
  218. return $this->error($validate->getError());
  219. }
  220. $sql = trim($param['sql']);
  221. if(!empty($sql)){
  222. $forbidden_keywords = ['into dumpfile', 'into outfile', 'char(', 'load_file'];
  223. foreach ($forbidden_keywords as $keyword) {
  224. if (stripos($sql, $keyword) !== false) {
  225. return $this->error(lang('format_err'));
  226. }
  227. }
  228. $sql = str_replace('{pre}',config('database.prefix'),$sql);
  229. //查询语句返回结果集
  230. if(
  231. strtolower(substr($sql,0,6))=="select" ||
  232. stripos($sql, ' outfile') !== false
  233. ){
  234. }
  235. else{
  236. Db::execute($sql);
  237. }
  238. }
  239. $this->success(lang('run_ok'));
  240. }
  241. return $this->fetch('admin@database/sql');
  242. }
  243. public function columns()
  244. {
  245. $param = input();
  246. $table = $param['table'];
  247. if (!empty($table) && !$this->isValidTable($table)) {
  248. return $this->error('Table is invalid.');
  249. }
  250. if (!empty($table)) {
  251. $list = Db::query('SHOW COLUMNS FROM ' . $table);
  252. $this->success(lang('obtain_ok'),null, $list);
  253. }
  254. $this->error(lang('param_err'));
  255. }
  256. public function rep()
  257. {
  258. if($this->request->isPost()){
  259. $param = input();
  260. $table = $param['table'];
  261. $field = $param['field'];
  262. $findstr = $param['findstr'];
  263. $tostr = $param['tostr'];
  264. $where = $param['where'];
  265. $validate = \think\Loader::validate('Token');
  266. if(!$validate->check($param)){
  267. return $this->error($validate->getError());
  268. }
  269. if (!empty($table) && !$this->isValidTable($table)) {
  270. return $this->error('Table is invalid.');
  271. }
  272. if(!empty($field) && !empty($findstr) && !empty($tostr)){
  273. $sql = "UPDATE ".$table." set ".$field."=Replace(".$field.",'".$findstr."','".$tostr."') where 1=1 ". $where;
  274. Db::execute($sql);
  275. return $this->success(lang('run_ok'));
  276. }
  277. return $this->error(lang('param_err'));
  278. }
  279. $list = Db::query("SHOW TABLE STATUS");
  280. $this->assign('list',$list);
  281. return $this->fetch('admin@database/rep');
  282. }
  283. private function isValidTable($table) {
  284. $list = Db::query("SHOW TABLE STATUS");
  285. foreach ($list as $table_raw) {
  286. if ($table_raw['Name'] == $table) {
  287. return true;
  288. }
  289. }
  290. return false;
  291. }
  292. }