PageRenderTime 57ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/upload/admin/includes/cls_sql_dump.php

https://github.com/bluelovers/ECShop
PHP | 504 lines | 307 code | 59 blank | 138 comment | 47 complexity | 87764d94b33a21b81850ac7a92fa9777 MD5 | raw file
  1. <?php
  2. /**
  3. * ECSHOP 数据库导出类
  4. * ============================================================================
  5. * 版权所有 2005-2010 上海商派网络科技有限公司,并保留所有权利。
  6. * 网站地址: http://www.ecshop.com;
  7. * ----------------------------------------------------------------------------
  8. * 这不是一个自由软件!您只能在不用于商业目的的前提下对程序代码进行修改和
  9. * 使用;不允许对程序代码以任何形式任何目的的再发布。
  10. * ============================================================================
  11. * $Author: liuhui $
  12. * $Id: cls_sql_dump.php 17063 2010-03-25 06:35:46Z liuhui $
  13. */
  14. if (!defined('IN_ECS'))
  15. {
  16. die('Hacking attempt');
  17. }
  18. /**
  19. * 对mysql敏感字符串转义
  20. *
  21. * @access public
  22. * @param string $str
  23. *
  24. * @return string
  25. */
  26. function dump_escape_string($str)
  27. {
  28. return cls_mysql::escape_string($str);
  29. }
  30. /**
  31. * 对mysql记录中的null值进行处理
  32. *
  33. * @access public
  34. * @param string $str
  35. *
  36. * @return string
  37. */
  38. function dump_null_string($str)
  39. {
  40. if (!isset($str) || is_null($str))
  41. {
  42. $str = 'NULL';
  43. }
  44. return $str;
  45. }
  46. class cls_sql_dump
  47. {
  48. var $max_size = 2097152; // 2M
  49. var $is_short = false;
  50. var $offset = 300;
  51. var $dump_sql = '';
  52. var $sql_num = 0;
  53. var $error_msg = '';
  54. var $db;
  55. /**
  56. * 类的构造函数
  57. *
  58. * @access public
  59. * @param
  60. *
  61. * @return void
  62. */
  63. function cls_sql_dump(&$db, $max_size=0)
  64. {
  65. $this->db = &$db;
  66. if ($max_size > 0 )
  67. {
  68. $this->max_size = $max_size;
  69. }
  70. }
  71. /**
  72. * 类的构造函数
  73. *
  74. * @access public
  75. * @param
  76. *
  77. * @return void
  78. */
  79. function __construct(&$db, $max_size =0)
  80. {
  81. $this->cls_sql_dump($db, $max_size);
  82. }
  83. /**
  84. * 获取指定表的定义
  85. *
  86. * @access public
  87. * @param string $table 数据表名
  88. * @param boolen $add_drop 是否加入drop table
  89. *
  90. * @return string $sql
  91. */
  92. function get_table_df($table, $add_drop = false)
  93. {
  94. if ($add_drop)
  95. {
  96. $table_df = "DROP TABLE IF EXISTS `$table`;\r\n";
  97. }
  98. else
  99. {
  100. $table_df = '';
  101. }
  102. $tmp_arr = $this->db->getRow("SHOW CREATE TABLE `$table`");
  103. $tmp_sql = $tmp_arr['Create Table'];
  104. $tmp_sql = substr($tmp_sql, 0, strrpos($tmp_sql, ")") + 1); //去除行尾定义。
  105. if ($this->db->version() >= '4.1')
  106. {
  107. $table_df .= $tmp_sql . " ENGINE=MyISAM DEFAULT CHARSET=" . str_replace('-', '', EC_CHARSET) . ";\r\n";
  108. }
  109. else
  110. {
  111. $table_df .= $tmp_sql . " TYPE=MyISAM;\r\n";
  112. }
  113. return $table_df;
  114. }
  115. /**
  116. * 获取指定表的数据定义
  117. *
  118. * @access public
  119. * @param string $table 表名
  120. * @param int $pos 备份开始位置
  121. *
  122. * @return int $post_pos 记录位置
  123. */
  124. function get_table_data($table, $pos)
  125. {
  126. $post_pos = $pos;
  127. /* 获取数据表记录总数 */
  128. $total = $this->db->getOne("SELECT COUNT(*) FROM $table");
  129. if ($total == 0 || $pos >= $total)
  130. {
  131. /* 无须处理 */
  132. return -1;
  133. }
  134. /* 确定循环次数 */
  135. $cycle_time = ceil(($total-$pos) / $this->offset); //每次取offset条数。需要取的次数
  136. /* 循环查数据表 */
  137. for($i = 0; $i<$cycle_time; $i++)
  138. {
  139. /* 获取数据库数据 */
  140. $data = $this->db->getAll("SELECT * FROM $table LIMIT " . ($this->offset * $i + $pos) . ', ' . $this->offset);
  141. $data_count = count($data);
  142. $fields = array_keys($data[0]);
  143. $start_sql = "INSERT INTO `$table` ( `" . implode("`, `", $fields) . "` ) VALUES ";
  144. /* 循环将数据写入 */
  145. for($j=0; $j< $data_count; $j++)
  146. {
  147. $record = array_map("dump_escape_string", $data[$j]); //过滤非法字符
  148. $record = array_map("dump_null_string", $record); //处理null值
  149. /* 检查是否能写入,能则写入 */
  150. if ($this->is_short)
  151. {
  152. if ($post_pos == $total-1)
  153. {
  154. $tmp_dump_sql = " ( '" . implode("', '" , $record) . "' );\r\n";
  155. }
  156. else
  157. {
  158. if ($j == $data_count - 1)
  159. {
  160. $tmp_dump_sql = " ( '" . implode("', '" , $record) . "' );\r\n";
  161. }
  162. else
  163. {
  164. $tmp_dump_sql = " ( '" . implode("', '" , $record) . "' ),\r\n";
  165. }
  166. }
  167. if ($post_pos == $pos)
  168. {
  169. /* 第一次插入数据 */
  170. $tmp_dump_sql = $start_sql . "\r\n" . $tmp_dump_sql;
  171. }
  172. else
  173. {
  174. if ($j == 0)
  175. {
  176. $tmp_dump_sql = $start_sql . "\r\n" . $tmp_dump_sql;
  177. }
  178. }
  179. }
  180. else
  181. {
  182. $tmp_dump_sql = $start_sql . " ('" . implode("', '" , $record) . "');\r\n";
  183. }
  184. $tmp_str_pos = strpos($tmp_dump_sql, 'NULL'); //把记录中null值的引号去掉
  185. $tmp_dump_sql = empty($tmp_str_pos) ? $tmp_dump_sql : substr($tmp_dump_sql, 0, $tmp_str_pos - 1) . 'NULL' . substr($tmp_dump_sql, $tmp_str_pos + 5);
  186. if (strlen($this->dump_sql) + strlen($tmp_dump_sql) > $this->max_size - 32)
  187. {
  188. if ($this->sql_num == 0)
  189. {
  190. $this->dump_sql .= $tmp_dump_sql; //当是第一条记录时强制写入
  191. $this->sql_num++;
  192. $post_pos++;
  193. if ($post_pos == $total)
  194. {
  195. /* 所有数据已经写完 */
  196. return -1;
  197. }
  198. }
  199. return $post_pos;
  200. }
  201. else
  202. {
  203. $this->dump_sql .= $tmp_dump_sql;
  204. $this->sql_num++; //记录sql条数
  205. $post_pos++;
  206. }
  207. }
  208. }
  209. /* 所有数据已经写完 */
  210. return -1;
  211. }
  212. /**
  213. * 备份一个数据表
  214. *
  215. * @access public
  216. * @param string $path 保存路径表名的文件
  217. * @param int $vol 卷标
  218. *
  219. * @return array $tables 未备份完的表列表
  220. */
  221. function dump_table($path, $vol)
  222. {
  223. $tables = $this->get_tables_list($path);
  224. if ($tables === false)
  225. {
  226. return false;
  227. }
  228. if (empty($tables))
  229. {
  230. return $tables;
  231. }
  232. $this->dump_sql = $this->make_head($vol);
  233. foreach($tables as $table => $pos)
  234. {
  235. if ($pos == -1)
  236. {
  237. /* 获取表定义,如果没有超过限制则保存 */
  238. $table_df = $this->get_table_df($table, true);
  239. if (strlen($this->dump_sql) + strlen($table_df) > $this->max_size - 32)
  240. {
  241. if ($this->sql_num == 0)
  242. {
  243. /* 第一条记录,强制写入 */
  244. $this->dump_sql .= $table_df;
  245. $this->sql_num +=2;
  246. $tables[$table] = 0;
  247. }
  248. /* 已经达到上限 */
  249. break;
  250. }
  251. else
  252. {
  253. $this->dump_sql .= $table_df;
  254. $this->sql_num +=2;
  255. $pos = 0;
  256. }
  257. }
  258. /* 尽可能多获取数据表数据 */
  259. $post_pos = $this->get_table_data($table, $pos);
  260. if ($post_pos == -1)
  261. {
  262. /* 该表已经完成,清除该表 */
  263. unset($tables[$table]);
  264. }
  265. else
  266. {
  267. /* 该表未完成。说明将要到达上限,记录备份数据位置 */
  268. $tables[$table] = $post_pos;
  269. break;
  270. }
  271. }
  272. $this->dump_sql .= '-- END ecshop v2.x SQL Dump Program ';
  273. $this->put_tables_list($path, $tables);
  274. return $tables;
  275. }
  276. /**
  277. * 生成备份文件头部
  278. *
  279. * @access public
  280. * @param int 文件卷数
  281. *
  282. * @return string $str 备份文件头部
  283. */
  284. function make_head($vol)
  285. {
  286. /* 系统信息 */
  287. $sys_info['os'] = PHP_OS;
  288. $sys_info['web_server'] = $GLOBALS['ecs']->get_domain();
  289. $sys_info['php_ver'] = PHP_VERSION;
  290. $sys_info['mysql_ver'] = $this->db->version();
  291. $sys_info['date'] = date('Y-m-d H:i:s');
  292. $head = "-- ecshop v2.x SQL Dump Program\r\n".
  293. "-- " . $sys_info['web_server'] . "\r\n".
  294. "-- \r\n".
  295. "-- DATE : ".$sys_info["date"]."\r\n".
  296. "-- MYSQL SERVER VERSION : ".$sys_info['mysql_ver']."\r\n".
  297. "-- PHP VERSION : ".$sys_info['php_ver']."\r\n".
  298. "-- ECShop VERSION : ".VERSION."\r\n".
  299. "-- Vol : " . $vol . "\r\n";
  300. return $head;
  301. }
  302. /**
  303. * 获取备份文件信息
  304. *
  305. * @access public
  306. * @param string $path 备份文件路径
  307. *
  308. * @return array $arr 信息数组
  309. */
  310. function get_head($path)
  311. {
  312. /* 获取sql文件头部信息 */
  313. $sql_info = array('date'=>'', 'mysql_ver'=> '', 'php_ver'=>0, 'ecs_ver'=>'', 'vol'=>0);
  314. $fp = fopen($path,'rb');
  315. $str = fread($fp, 250);
  316. fclose($fp);
  317. $arr = explode("\n", $str);
  318. foreach ($arr AS $val)
  319. {
  320. $pos = strpos($val, ':');
  321. if ($pos > 0)
  322. {
  323. $type = trim(substr($val, 0, $pos), "-\n\r\t ");
  324. $value = trim(substr($val, $pos+1), "/\n\r\t ");
  325. if ($type == 'DATE')
  326. {
  327. $sql_info['date'] = $value;
  328. }
  329. elseif ($type == 'MYSQL SERVER VERSION')
  330. {
  331. $sql_info['mysql_ver'] = $value;
  332. }
  333. elseif ($type == 'PHP VERSION')
  334. {
  335. $sql_info['php_ver'] = $value;
  336. }
  337. elseif ($type == 'ECShop VERSION')
  338. {
  339. $sql_info['ecs_ver'] = $value;
  340. }
  341. elseif ($type == 'Vol')
  342. {
  343. $sql_info['vol'] = $value;
  344. }
  345. }
  346. }
  347. return $sql_info;
  348. }
  349. /**
  350. * 将文件中数据表列表取出
  351. *
  352. * @access public
  353. * @param string $path 文件路径
  354. *
  355. * @return array $arr 数据表列表
  356. */
  357. function get_tables_list($path)
  358. {
  359. if (!file_exists($path))
  360. {
  361. $this->error_msg = $path . ' is not exists';
  362. return false;
  363. }
  364. $arr = array();
  365. $str = @file_get_contents($path);
  366. if (!empty($str))
  367. {
  368. $tmp_arr = explode("\n", $str);
  369. foreach ($tmp_arr as $val)
  370. {
  371. $val = trim ($val, "\r;");
  372. if (!empty($val))
  373. {
  374. list($table, $count) = explode(':',$val);
  375. $arr[$table] = $count;
  376. }
  377. }
  378. }
  379. return $arr;
  380. }
  381. /**
  382. * 将数据表数组写入指定文件
  383. *
  384. * @access public
  385. * @param string $path 文件路径
  386. * @param array $arr 要写入的数据
  387. *
  388. * @return boolen
  389. */
  390. function put_tables_list($path, $arr)
  391. {
  392. if (is_array($arr))
  393. {
  394. $str = '';
  395. foreach($arr as $key => $val)
  396. {
  397. $str .= $key . ':' . $val . ";\r\n";
  398. }
  399. if (@file_put_contents($path, $str))
  400. {
  401. return true;
  402. }
  403. else
  404. {
  405. $this->error_msg = 'Can not write ' . $path;
  406. return false;
  407. }
  408. }
  409. else
  410. {
  411. $this->error_msg = 'It need a array';
  412. return false;
  413. }
  414. }
  415. /**
  416. * 返回一个随机的名字
  417. *
  418. * @access public
  419. * @param
  420. *
  421. * @return string $str 随机名称
  422. */
  423. function get_random_name()
  424. {
  425. $str = date('Ymd');
  426. for ($i = 0; $i < 6; $i++)
  427. {
  428. $str .= chr(mt_rand(97, 122));
  429. }
  430. return $str;
  431. }
  432. /**
  433. * 返回错误信息
  434. *
  435. * @access public
  436. * @param
  437. *
  438. * @return void
  439. */
  440. function errorMsg()
  441. {
  442. return $this->error_msg;
  443. }
  444. }
  445. ?>