db.model.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. <?php
  2. /**
  3. * 数据库操作 模型
  4. *
  5. * session中存放的变量说明:$_SESSION['db_backup']
  6. * size 文件大小,单位B, 即 传入参数*1024
  7. * table_name 当前进行备份的表名,即从该表名的位置进行备份,默认为空,即从开头进行备份
  8. * op 当前表建立动作,create/insert 两种 默认为create
  9. * limit 条数记录,反映当前insert语句中,处理到的位置,默认为0
  10. * back_file 备份目录名 格式为YYYYMMDD_序号
  11. * backup_tables 需要备份的表名列表,为数组格式
  12. * md5 加密后缀
  13. *
  14. */
  15. defined('InShopNC') or exit('Access Invalid!');
  16. class dbModel{
  17. /**
  18. * 备份语句
  19. */
  20. private $back_content = '';
  21. /**
  22. * 步骤
  23. */
  24. private $step = 1;
  25. /**
  26. * 备份数据库
  27. *
  28. * @param int $step 步骤
  29. * @return string 字符类型的返回结果
  30. */
  31. public function backUp($step=1){
  32. $table_list = $_SESSION['db_backup']['backup_tables'];
  33. if ($step == 1){
  34. $this->back_content .= "\r\nSET SQL_MODE='NO_AUTO_VALUE_ON_ZERO';\r\n\r\n";
  35. $_SESSION['db_backup']['table_name'] = $_SESSION['db_backup']['backup_tables'][0];
  36. }
  37. $this->step = $step;
  38. if (!empty($_SESSION['db_backup']['table_name'])){
  39. $key = array_search($_SESSION['db_backup']['table_name'],$table_list);
  40. if ($key > 0){
  41. for ($i=0; $i<$key; $i++){
  42. unset($table_list[$i]);
  43. }
  44. }
  45. }
  46. /**
  47. * 创建表sql
  48. */
  49. if ($_SESSION['db_backup']['op'] == 'create'){
  50. foreach ($table_list as $k => $v){
  51. /**
  52. * 写入session
  53. */
  54. $_SESSION['db_backup']['table_name'] = $v;
  55. $result = $this->getCreateContent($v);
  56. /**
  57. * 跳出
  58. */
  59. if ($result === false){
  60. return true;
  61. }
  62. /**
  63. * 判断是否最后结束
  64. */
  65. if (empty($table_list[$k+1])){
  66. /**
  67. * 写入文件
  68. */
  69. $this->writeBackFile();
  70. /**
  71. * 写入session op
  72. */
  73. $_SESSION['db_backup']['op'] = 'insert';
  74. $_SESSION['db_backup']['table_name'] = $_SESSION['db_backup']['backup_tables'][0];
  75. return true;
  76. }
  77. }
  78. }
  79. /**
  80. * 表信息sql
  81. */
  82. if ($_SESSION['db_backup']['op'] == 'insert'){
  83. foreach ($table_list as $k => $v){
  84. /**
  85. * 写入session
  86. */
  87. $_SESSION['db_backup']['table_name'] = $v;
  88. while (true) {
  89. $result = $this->getInsertContent($v);
  90. if ($result === 'succ'){
  91. break;
  92. }
  93. /**
  94. * 跳出
  95. */
  96. if ($result === false){
  97. return true;
  98. }
  99. }
  100. /**
  101. * 判断是否最后结束
  102. */
  103. if (empty($table_list[$k+1]) && $_SESSION['db_backup']['limit'] == 0){
  104. /**
  105. * 写入文件
  106. */
  107. $this->writeBackFile();
  108. return true;
  109. }
  110. }
  111. }
  112. return true;
  113. }
  114. /**
  115. * 表名列表
  116. *
  117. * @param string $type all/self 取表列表范围,all为整个数据库全部表,self为产品自己的表
  118. * @return array $rs_row 返回数组形式的查询结果
  119. */
  120. public function getTableList($type = 'self'){
  121. $table_list = array();
  122. $tmp = Db::showTables();
  123. $count = strlen(DBPRE);
  124. $table = "Tables_in_" . DBNAME;
  125. if ($type == 'all') {
  126. if (is_array($tmp)) {
  127. foreach ($tmp as $k => $v) {
  128. $table_list[] = $v[$table];
  129. }
  130. }
  131. }
  132. if ($type == 'self') {
  133. if (is_array($tmp)) {
  134. foreach ($tmp as $k => $v) {
  135. $table_list[] = $v[$table];
  136. }
  137. }
  138. }
  139. return $table_list;
  140. }
  141. /**
  142. * create表语句内容
  143. *
  144. * @param
  145. * @return string 字符串类型的返回结果
  146. */
  147. private function getCreateContent($table){
  148. /**
  149. * 构造语句
  150. */
  151. $tmp_create = "DROP TABLE IF EXISTS `". $table ."`;\r\n";
  152. $tmp_create .= Db::showCreateTable(substr($table,strlen(DBPRE),strlen($table)-1));
  153. $tmp_create .= ";\r\n\r\n";
  154. /**
  155. * 判断长度
  156. */
  157. if (strlen($this->back_content.$tmp_create) >= $_SESSION['db_backup']['size']){
  158. /**
  159. * 写入文件,跳出
  160. */
  161. $this->writeBackFile();
  162. /**
  163. * 停止备份
  164. */
  165. return false;
  166. }else {
  167. /**
  168. * 增加备份语句
  169. */
  170. $this->back_content .= $tmp_create;
  171. /**
  172. * 继续备份
  173. */
  174. return true;
  175. }
  176. }
  177. /**
  178. * 转换特殊符号
  179. *
  180. * @param
  181. * @return string 字符串类型的返回结果
  182. */
  183. private function sqlAddslashes($a_string = '', $is_like = false, $crlf = false, $php_code = false){
  184. if ($is_like) {
  185. $a_string = str_replace('\\', '\\\\\\\\', $a_string);
  186. } else {
  187. $a_string = str_replace('\\', '\\\\', $a_string);
  188. }
  189. if ($crlf) {
  190. $a_string = str_replace("\n", '\n', $a_string);
  191. $a_string = str_replace("\r", '\r', $a_string);
  192. $a_string = str_replace("\t", '\t', $a_string);
  193. }
  194. if ($php_code) {
  195. $a_string = str_replace('\'', '\\\'', $a_string);
  196. } else {
  197. $a_string = str_replace('\'', '\'\'', $a_string);
  198. }
  199. return $a_string;
  200. }
  201. /**
  202. * insert表语句内容
  203. *
  204. * @param
  205. * @return string 字符串类型的返回结果
  206. */
  207. private function getInsertContent($table){
  208. /**
  209. * 开始条数
  210. */
  211. $limit = $_SESSION['db_backup']['limit']?$_SESSION['db_backup']['limit']:0;
  212. /**
  213. * 当前语句大小
  214. */
  215. $now_size = strlen($this->back_content);
  216. /**
  217. * 取信息
  218. */
  219. $param = array();
  220. $param['table'] = substr($table,strlen(DBPRE),strlen($table)-1);
  221. $param['limit'] = $limit.',300';
  222. $param['cache'] = false;
  223. $list = Db::select($param);
  224. /**
  225. * 没有信息
  226. */
  227. if (empty($list)){
  228. /**
  229. * limit数据归0
  230. */
  231. $_SESSION['db_backup']['limit'] = 0;
  232. /**
  233. * 继续back循环
  234. */
  235. return 'succ';
  236. }
  237. /**
  238. * 字段信息
  239. */
  240. $columns_array = Db::showColumns(substr($table,strlen(DBPRE),strlen($table)-1));
  241. /**
  242. * 生成sql语句
  243. */
  244. $result = '';
  245. foreach ($list as $k => $v){
  246. $tmp_sql = '';
  247. $tmp_columns = '';
  248. $tmp_value = '';
  249. /**
  250. * 字段信息
  251. */
  252. foreach ($columns_array as $k_col => $v_col){
  253. /**
  254. * 字段
  255. */
  256. $tmp_columns .= "`". $k_col ."`,";
  257. /**
  258. * 值
  259. */
  260. if ($v_col['null'] == 'YES'){
  261. if (empty($v[$k_col])){
  262. $tmp_value .= "NULL,";
  263. }else {
  264. $tmp_value .= "'". $this->sqlAddslashes($v[$k_col]) ."',";
  265. }
  266. }else {
  267. $tmp_value .= "'". $this->sqlAddslashes($v[$k_col]) ."',";
  268. }
  269. }
  270. /**
  271. * 构造语句
  272. */
  273. $tmp_sql .= "INSERT INTO `".$table."` ";
  274. $tmp_sql .= "(";
  275. $tmp_sql .= trim($tmp_columns,',');
  276. $tmp_sql .= ") VALUES(";
  277. $tmp_sql .= trim($tmp_value,',');
  278. $tmp_sql .= ")";
  279. $tmp_sql .= ";\r\n";
  280. /**
  281. * 判断字符串大小限制
  282. */
  283. if (strlen($this->back_content.$tmp_sql) >= $_SESSION['db_backup']['size']){
  284. /**
  285. * 备份文件
  286. */
  287. $this->writeBackFile();
  288. /**
  289. * 超过限制,跳出讯号
  290. */
  291. return false;
  292. }else {
  293. /**
  294. * 增加session中的limit计数
  295. */
  296. $_SESSION['db_backup']['limit']++;
  297. $this->back_content .= $tmp_sql;
  298. }
  299. }
  300. /**
  301. * 结束该表的读取,limit归0
  302. */
  303. if (count($list) < 10){
  304. $_SESSION['db_backup']['limit'] = 0;
  305. $this->back_content .= "\r\n";
  306. return 'succ';
  307. }
  308. /**
  309. * 继续back循环
  310. */
  311. return true;
  312. }
  313. /**
  314. * 获取目录
  315. *
  316. * @param
  317. * @return
  318. */
  319. public function getBackDir(){
  320. /**
  321. * 按照日期进行生成
  322. */
  323. $dir_list = readDirList(BASE_ROOT_PATH.DS.'sql_back');
  324. $tmp = date('Ymd');
  325. $check_array = array();
  326. if (is_array($dir_list)){
  327. foreach ($dir_list as $k => $v){
  328. if (substr($v,0,strlen($tmp)) == $tmp){
  329. $check_array[] = substr($v,strlen($tmp)+1,strlen($v));
  330. }
  331. }
  332. }
  333. $return = $tmp.'_'.($check_array[count($check_array)-1]+1);
  334. return $return;
  335. }
  336. /**
  337. * 写入文件
  338. *
  339. * @param int $id 记录ID
  340. * @return array $rs_row 返回数组形式的查询结果
  341. */
  342. public function writeBackFile(){
  343. Language::read('model_lang_index');
  344. $lang = Language::getLangContent();
  345. $step = $this->step;
  346. try {
  347. if (!is_dir(BASE_ROOT_PATH.DS.'sql_back'.DS.$_SESSION['db_backup']['back_file'])){
  348. if (!$this->mkdirs(BASE_ROOT_PATH.DS.'sql_back'.DS.$_SESSION['db_backup']['back_file'],0755)){
  349. $error = $lang['db_backup_mkdir_fail'];
  350. throw new Exception($error);
  351. }else {
  352. $fp = @fopen(BASE_ROOT_PATH.DS.'sql_back'.DS.$_SESSION['db_backup']['back_file'].DS.'index.html','w+');
  353. @fclose($fp);
  354. }
  355. }
  356. $file_name = BASE_ROOT_PATH.DS.'sql_back'.DS.$_SESSION['db_backup']['back_file'].DS.$_SESSION['db_backup']['back_file'].'_'.$step.'_'.$_SESSION['db_backup']['md5'].'.sql';
  357. $fp = @fopen($file_name,'w+');
  358. if (@fwrite($fp,$this->back_content) === false){
  359. $error = $lang['db_backup_vi_file_fail'];
  360. throw new Exception($error);
  361. }
  362. @fclose($fp);
  363. }catch (Exception $e){
  364. showMessage($e->getMessage(),'','exception');
  365. }
  366. return true;
  367. }
  368. /**
  369. * 建立多级目录
  370. *
  371. * @param string $dir 目录
  372. * @return true
  373. */
  374. public function mkdirs($dir){
  375. if (!is_dir($dir)) {
  376. if (!$this->mkdirs(dirname($dir))) {
  377. return false;
  378. }
  379. if (!mkdir($dir, 0777)) {
  380. return false;
  381. }
  382. }
  383. return true;
  384. }
  385. /**
  386. * 数据库备份导入
  387. *
  388. * @param string $path 目录
  389. * @param int $step 步骤,也是第几个文件
  390. * @return array $rs_row 返回数组形式的查询结果
  391. */
  392. public function import($path,$step=1){
  393. $dir = BASE_ROOT_PATH.DS.'sql_back'.DS.$path;
  394. $file_list = array();
  395. readFileList($dir,$file_list);
  396. /**
  397. * 过滤文件
  398. */
  399. if (!empty($file_list) && is_array($file_list)){
  400. foreach ($file_list as $key=>$file_name){
  401. if (strtolower(substr($file_name,-4)) == '.sql'){
  402. $tmp_list[] = $file_name;
  403. }
  404. }
  405. $file_list = $tmp_list;
  406. }
  407. foreach($file_list as $k=>$v){
  408. $varr = explode('_',$v);
  409. $file_list[$k] = $varr['0'].'_'.$varr['1'].'_'.$varr['2'].'_'.$varr['3'].'_'.($k+1).'_'.$varr['5'];
  410. }
  411. $file_name = $file_list[$step-1];
  412. //此处使用is_file来判断该sql文件是否存在,不使用file_exists
  413. if (is_file($file_name)){
  414. $handle = @fopen($file_name, "r");
  415. $tmp_sql = '';
  416. if ($handle) {
  417. while (!feof($handle)) {
  418. $buffer = fgets($handle);
  419. if (trim($buffer) != ''){
  420. $tmp_sql .= $buffer;
  421. if (substr(rtrim($buffer),-1) == ';'){
  422. if (preg_match('/^(CREATE|ALTER|DROP)\s+(VIEW|TABLE|DATABASE|SCHEMA)\s+/i', ltrim($tmp_sql))){
  423. //标准的SQL语句,将被执行
  424. }else if (preg_match('/^(INSERT)\s+(INTO)\s+/i', ltrim($tmp_sql)) && substr(rtrim($buffer),-2) == ');'){
  425. //标准的SQL语句,将被执行
  426. }else if (preg_match('/^(SET)\s+SQL_MODE=/i', ltrim($tmp_sql))){
  427. //SET SQL_MODE 设置,将被执行
  428. }else{
  429. //不能组成标准的SQL语句,继续向下一行取内容,直到组成合法的SQL为止
  430. continue;
  431. }
  432. if (!empty($tmp_sql)){
  433. /**
  434. * 销毁当前用户Session信息
  435. */
  436. if (strpos($tmp_sql,cookie('sess_id')) !== false){
  437. unset($tmp_sql);
  438. continue;
  439. }
  440. Db::query($tmp_sql,'slave');
  441. unset($tmp_sql);
  442. }
  443. }
  444. }
  445. }
  446. @fclose($handle);
  447. }
  448. /**
  449. * 判断是否还有下个文件
  450. */
  451. if (empty($file_list[$step])){
  452. return 'succ';
  453. }else {
  454. return 'continue';
  455. }
  456. }else {
  457. return false;
  458. }
  459. }
  460. }