PageRenderTime 63ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/App/Modules/Admin/Action/DatabaseAction.class.php

https://gitlab.com/shenzhenlong1203/GZTravel
PHP | 297 lines | 223 code | 31 blank | 43 comment | 57 complexity | 724655a41901cc96fb8f2983433c0a99 MD5 | raw file
Possible License(s): Apache-2.0, MPL-2.0-no-copyleft-exception
  1. <?php
  2. /**
  3. * 数据库备份恢复类
  4. * @author <[s@easycms.cc]>
  5. */
  6. class DatabaseAction extends Action {
  7. public function _initialize() {
  8. if (!isset($_SESSION[C('USER_AUTH_KEY')])) {
  9. $this->redirect('Admin/Login/login');
  10. }
  11. $notAuth=in_array(MODULE_NAME, explode(',', C('NOT_AUTH_MODULE'))) || in_array(ACTION_NAME, explode(',', C('NOT_AUTH_ACTION')));
  12. if (C('USER_AUTH_ON') && !$notAuth) {
  13. import('ORG.Util.RBAC');
  14. RBAC::AccessDecision(GROUP_NAME) || $this->error('没有权限');//分组模式必须加该参数
  15. }
  16. }
  17. /**
  18. * 数据库备份/还原列表
  19. * @param String $type import-还原,export-备份
  20. */
  21. public function index($type = 'export'){
  22. switch ($type) {
  23. /* 数据还原 */
  24. case 'import':
  25. //列出备份文件列表
  26. import("ORG.Io.Dir");
  27. $path = realpath(C('DATA_BACKUP_PATH'));
  28. $dir = new Dir($path, '*.sql.gz');
  29. $glob = $dir->toArray();
  30. $list = array();
  31. foreach ($glob as $file) {
  32. $name = $file['filename'];
  33. if(preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql(?:\.gz)?$/', $name)){
  34. $fileSize = $file['size'];
  35. $name = sscanf($name, '%4s%2s%2s-%2s%2s%2s-%d');
  36. $date = "{$name[0]}-{$name[1]}-{$name[2]}";
  37. $time = "{$name[3]}:{$name[4]}:{$name[5]}";
  38. $part = $name[6];
  39. if(isset($list["{$date} {$time}"])){
  40. $info = $list["{$date} {$time}"];
  41. $info['part'] = max($info['part'], $part);
  42. $info['size'] = $info['size'] + $fileSize;
  43. } else {
  44. $info['part'] = $part;
  45. $info['size'] = $fileSize;
  46. }
  47. $extension = strtoupper($file['ext']);
  48. $info['compress'] = ($extension === 'SQL') ? '-' : $extension;
  49. $info['time'] = strtotime("{$date} {$time}");
  50. $list["{$date} {$time}"] = $info;
  51. }
  52. }
  53. break;
  54. /* 数据备份 */
  55. case 'export':
  56. $Db = M();
  57. $list = $Db->query('SHOW TABLE STATUS');
  58. $list = array_map('array_change_key_case', $list);
  59. break;
  60. default:
  61. $this->error('参数错误!');
  62. }
  63. //渲染模板
  64. $this->assign('meta_title', $title);
  65. $this->assign('list', $list);
  66. $this->display($type);
  67. }
  68. /**
  69. * 优化表
  70. * @param String $tables 表名
  71. */
  72. public function optimize($tables = null){
  73. if($tables) {
  74. $Db = M();
  75. if(is_array($tables)){
  76. $tables = implode('`,`', $tables);
  77. $list = $Db->query("OPTIMIZE TABLE `{$tables}`");
  78. if($list){
  79. $this->success("数据表优化完成!");
  80. } else {
  81. $this->error("数据表优化出错请重试!");
  82. }
  83. } else {
  84. $list = $Db->query("OPTIMIZE TABLE `{$tables}`");
  85. if($list){
  86. $this->success("数据表'{$tables}'优化完成!");
  87. } else {
  88. $this->error("数据表'{$tables}'优化出错请重试!");
  89. }
  90. }
  91. } else {
  92. $this->error("请指定要优化的表!");
  93. }
  94. }
  95. /**
  96. * 修复表
  97. * @param String $tables 表名
  98. */
  99. public function repair($tables = null){
  100. if($tables) {
  101. $Db = M();
  102. if(is_array($tables)){
  103. $tables = implode('`,`', $tables);
  104. $list = $Db->query("REPAIR TABLE `{$tables}`");
  105. if($list){
  106. $this->success("数据表修复完成!");
  107. } else {
  108. $this->error("数据表修复出错请重试!");
  109. }
  110. } else {
  111. $list = $Db->query("REPAIR TABLE `{$tables}`");
  112. if($list){
  113. $this->success("数据表'{$tables}'修复完成!");
  114. } else {
  115. $this->error("数据表'{$tables}'修复出错请重试!");
  116. }
  117. }
  118. } else {
  119. $this->error("请指定要修复的表!");
  120. }
  121. }
  122. /**
  123. * 删除备份文件
  124. * @param Integer $time 备份时间
  125. */
  126. public function del($time = 0){
  127. if($time){
  128. $name = date('Ymd-His', $time) . '-*.sql*';
  129. $path = realpath(C('DATA_BACKUP_PATH')) . DIRECTORY_SEPARATOR . $name;
  130. array_map("unlink", glob($path));
  131. if(count(glob($path))){
  132. $this->success('备份文件删除失败,请检查权限!');
  133. } else {
  134. $this->success('备份文件删除成功!');
  135. }
  136. } else {
  137. $this->error('参数错误!');
  138. }
  139. }
  140. /**
  141. * 备份数据库
  142. * @param String $tables 表名
  143. * @param Integer $id 表ID
  144. * @param Integer $start 起始行数
  145. */
  146. public function export($tables = null, $id = null, $start = null){
  147. import('ORG.Net.Database');
  148. if(IS_POST && !empty($tables) && is_array($tables)){ //初始化
  149. //读取备份配置
  150. $config = array(
  151. 'path' => realpath(C('DATA_BACKUP_PATH')) . DIRECTORY_SEPARATOR,
  152. 'part' => C('DATA_BACKUP_PART_SIZE'),
  153. 'compress' => C('DATA_BACKUP_COMPRESS'),
  154. 'level' => C('DATA_BACKUP_COMPRESS_LEVEL'),
  155. );
  156. //检查是否有正在执行的任务
  157. $lock = "{$config['path']}backup.lock";
  158. if(is_file($lock)){
  159. $this->error('检测到有一个备份任务正在执行,请稍后再试!');
  160. } else {
  161. //创建锁文件
  162. file_put_contents($lock, NOW_TIME);
  163. }
  164. //检查备份目录是否可写
  165. is_writeable($config['path']) || $this->error('备份目录不存在或不可写,请检查后重试!');
  166. session('backup_config', $config);
  167. //生成备份文件信息
  168. $file = array(
  169. 'name' => date('Ymd-His', NOW_TIME),
  170. 'part' => 1,
  171. );
  172. session('backup_file', $file);
  173. //缓存要备份的表
  174. session('backup_tables', $tables);
  175. //创建备份文件
  176. $Database = new Database($file, $config);
  177. if(false !== $Database->create()){
  178. $tab = array('id' => 0, 'start' => 0);
  179. $this->success('初始化成功!', '', array('tables' => $tables, 'tab' => $tab));
  180. } else {
  181. $this->error('初始化失败,备份文件创建失败!');
  182. }
  183. } elseif (IS_GET && is_numeric($id) && is_numeric($start)) { //备份数据
  184. $tables = session('backup_tables');
  185. //备份指定表
  186. $Database = new Database(session('backup_file'), session('backup_config'));
  187. $start = $Database->backup($tables[$id], $start);
  188. if(false === $start){ //出错
  189. $this->error('备份出错!');
  190. } elseif (0 === $start) { //下一表
  191. if(isset($tables[++$id])){
  192. $tab = array('id' => $id, 'start' => 0);
  193. $this->success('备份完成!', '', array('tab' => $tab));
  194. } else { //备份完成,清空缓存
  195. unlink(session('backup_config.path') . 'backup.lock');
  196. session('backup_tables', null);
  197. session('backup_file', null);
  198. session('backup_config', null);
  199. $this->success('备份完成!');
  200. }
  201. } else {
  202. $tab = array('id' => $id, 'start' => $start[0]);
  203. $rate = floor(100 * ($start[0] / $start[1]));
  204. $this->success("正在备份...({$rate}%)", '', array('tab' => $tab));
  205. }
  206. } else { //出错
  207. $this->error('参数错误!');
  208. }
  209. }
  210. /**
  211. * 还原数据库
  212. */
  213. public function import($time = 0, $part = null, $start = null){
  214. import('ORG.Net.Database');
  215. if(is_numeric($time) && is_null($part) && is_null($start)){ //初始化
  216. //获取备份文件信息
  217. $name = date('Ymd-His', $time) . '-*.sql*';
  218. $path = realpath(C('DATA_BACKUP_PATH')) . DIRECTORY_SEPARATOR . $name;
  219. $files = glob($path);
  220. $list = array();
  221. foreach($files as $name){
  222. $basename = basename($name);
  223. $match = sscanf($basename, '%4s%2s%2s-%2s%2s%2s-%d');
  224. $gz = preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql.gz$/', $basename);
  225. $list[$match[6]] = array($match[6], $name, $gz);
  226. }
  227. ksort($list);
  228. //检测文件正确性
  229. $last = end($list);
  230. if(count($list) === $last[0]){
  231. session('backup_list', $list); //缓存备份列表
  232. $this->success('数据库还原成功!', '', array('part' => 1, 'start' => 0));
  233. } else {
  234. $this->error('备份文件可能已经损坏,请检查!');
  235. }
  236. } elseif(is_numeric($part) && is_numeric($start)) {
  237. $list = session('backup_list');
  238. $db = new Database($list[$part], array(
  239. 'path' => realpath(C('DATA_BACKUP_PATH')) . DIRECTORY_SEPARATOR,
  240. 'compress' => $list[$part][2]));
  241. $start = $db->import($start);
  242. if(false === $start){
  243. $this->error('还原数据出错!');
  244. } elseif(0 === $start) { //下一卷
  245. if(isset($list[++$part])){
  246. $data = array('part' => $part, 'start' => 0);
  247. $this->success("正在还原...#{$part}", '', $data);
  248. } else {
  249. session('backup_list', null);
  250. $this->success('还原完成!');
  251. }
  252. } else {
  253. $data = array('part' => $part, 'start' => $start[0]);
  254. if($start[1]){
  255. $rate = floor(100 * ($start[0] / $start[1]));
  256. $this->success("正在还原...#{$part} ({$rate}%)", '', $data);
  257. } else {
  258. $data['gz'] = 1;
  259. $this->success("正在还原...#{$part}", '', $data);
  260. }
  261. }
  262. } else {
  263. $this->error('参数错误!');
  264. }
  265. }
  266. }