model.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. <?php
  2. /**
  3. * The model file of backup module of ZenTaoPMS.
  4. *
  5. * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
  6. * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
  7. * @author Yidong Wang <yidong@cnezsoft.com>
  8. * @package backup
  9. * @version $Id$
  10. * @link https://www.zentao.net
  11. */
  12. class backupModel extends model
  13. {
  14. /**
  15. * Backup SQL.
  16. *
  17. * @param string $backupFile
  18. * @access public
  19. * @return object
  20. */
  21. public function backSQL($backupFile)
  22. {
  23. $zdb = $this->app->loadClass('zdb');
  24. return $zdb->dump($backupFile);
  25. }
  26. /**
  27. * Backup file.
  28. *
  29. * @param string $backupFile
  30. * @access public
  31. * @return object
  32. */
  33. public function backFile($backupFile)
  34. {
  35. $zfile = $this->app->loadClass('zfile');
  36. $return = new stdclass();
  37. $return->result = true;
  38. $return->error = '';
  39. if(!is_dir($backupFile)) mkdir($backupFile, 0777, true);
  40. $tmpLogFile = $this->getTmpLogFile($backupFile);
  41. $dataDir = $this->app->getAppRoot() . 'www/data/';
  42. $count = $zfile->getCount($dataDir, array('course'));
  43. file_put_contents($tmpLogFile, json_encode(array('allCount' => $count)));
  44. $result = $zfile->copyDir($dataDir, $backupFile, $logLevel = false, $tmpLogFile, array('course'));
  45. $this->processSummary($backupFile, $result['count'], $result['size'], $result['errorFiles'], $count);
  46. unlink($tmpLogFile);
  47. return $return;
  48. }
  49. /**
  50. * Backup code.
  51. *
  52. * @param string $backupFile
  53. * @access public
  54. * @return object
  55. */
  56. public function backCode($backupFile)
  57. {
  58. $zfile = $this->app->loadClass('zfile');
  59. $return = new stdclass();
  60. $return->result = true;
  61. $return->error = '';
  62. $tmpLogFile = $this->getTmpLogFile($backupFile);
  63. $appRoot = $this->app->getAppRoot();
  64. $fileList = glob($appRoot . '*');
  65. $wwwFileList = glob($appRoot . 'www/*');
  66. $tmpFile = array_search($appRoot . 'tmp', $fileList);
  67. $wwwFile = array_search($appRoot . 'www', $fileList);
  68. $dataFile = array_search($appRoot . 'www/data', $wwwFileList);
  69. unset($fileList[$tmpFile]);
  70. unset($fileList[$wwwFile]);
  71. unset($wwwFileList[$dataFile]);
  72. $fileList = array_merge($fileList, $wwwFileList);
  73. if(!is_dir($backupFile)) mkdir($backupFile, 0777, true);
  74. $allCount = 0;
  75. foreach($fileList as $codeFile) $allCount += $zfile->getCount($codeFile);
  76. file_put_contents($tmpLogFile, json_encode(array('allCount' => $allCount)));
  77. $copiedCount = 0;
  78. $copiedSize = 0;
  79. $errorFiles = array();
  80. foreach($fileList as $codeFile)
  81. {
  82. $file = trim(str_replace($appRoot, '', $codeFile), DS);
  83. if(is_dir($codeFile))
  84. {
  85. if(!is_dir($backupFile . DS . $file)) mkdir($backupFile . DS . $file, 0777, true);
  86. $result = $zfile->copyDir($codeFile, $backupFile . DS . $file, $logLevel = false, $tmpLogFile);
  87. $copiedCount += $result['count'];
  88. $copiedSize += $result['size'];
  89. $errorFiles += $result['errorFiles'];
  90. }
  91. else
  92. {
  93. $dirName = dirname($file);
  94. if(!is_dir($backupFile . DS . $dirName)) mkdir($backupFile . DS . $dirName, 0777, true);
  95. if($zfile->copyFile($codeFile, $backupFile . DS . $file))
  96. {
  97. $copiedCount += 1;
  98. $copiedSize += filesize($codeFile);
  99. }
  100. else
  101. {
  102. $errorFiles[] = $codeFile;
  103. }
  104. }
  105. }
  106. $this->processSummary($backupFile, $copiedCount, $copiedSize, $errorFiles, $allCount);
  107. unlink($tmpLogFile);
  108. return $return;
  109. }
  110. /**
  111. * Restore SQL.
  112. *
  113. * @param string $backupFile
  114. * @access public
  115. * @return object
  116. */
  117. public function restoreSQL($backupFile)
  118. {
  119. $zdb = $this->app->loadClass('zdb');
  120. $allTables = $zdb->getAllTables();
  121. foreach($allTables as $tableName => $tableType)
  122. {
  123. try
  124. {
  125. $this->dbh->exec("DROP $tableType IF EXISTS `$tableName`");
  126. }
  127. catch(PDOException $e){}
  128. }
  129. $this->dao->clearCache();
  130. return $zdb->import($backupFile);
  131. }
  132. /**
  133. * Restore File.
  134. *
  135. * @param string $backupFile
  136. * @access public
  137. * @return object
  138. */
  139. public function restoreFile($backupFile)
  140. {
  141. $return = new stdclass();
  142. $return->result = true;
  143. $return->error = '';
  144. if(is_dir($backupFile))
  145. {
  146. $zfile = $this->app->loadClass('zfile');
  147. $zfile->copyDir($backupFile, $this->app->getAppRoot() . 'www/data/', $showDetails = false);
  148. }
  149. return $return;
  150. }
  151. /**
  152. * Add file header.
  153. *
  154. * @param string $fileName
  155. * @access public
  156. * @return bool
  157. */
  158. public function addFileHeader($fileName)
  159. {
  160. $die = "<?php die();?" . ">\n";
  161. $tmpFile = $fileName . '.tmp';
  162. rename($fileName, $tmpFile);
  163. file_put_contents($fileName, $die);
  164. $fh = fopen($tmpFile, 'r');
  165. $length = 2 * 1024 * 1024;
  166. while(!feof($fh))
  167. {
  168. $buff = fread($fh, $length);
  169. file_put_contents($fileName, $buff, FILE_APPEND);
  170. }
  171. fclose($fh);
  172. unlink($tmpFile);
  173. return true;
  174. }
  175. /**
  176. * Remove file header.
  177. *
  178. * @param string $fileName
  179. * @access public
  180. * @return bool
  181. */
  182. public function removeFileHeader($fileName)
  183. {
  184. $tmpFile = $fileName . '.tmp';
  185. $fh = fopen($fileName, 'r');
  186. $length = 2 * 1024 * 1024;
  187. $readedFirstLine = false;
  188. while(!feof($fh))
  189. {
  190. if(!$readedFirstLine)
  191. {
  192. fgets($fh);
  193. $readedFirstLine = true;
  194. continue;
  195. }
  196. $buff = fread($fh, $length);
  197. file_put_contents($tmpFile, $buff, FILE_APPEND);
  198. }
  199. fclose($fh);
  200. rename($tmpFile, $fileName);
  201. return true;
  202. }
  203. /**
  204. * Get dir size.
  205. *
  206. * @param string $backup
  207. * @access public
  208. * @return array
  209. */
  210. public function getBackupSummary($backup)
  211. {
  212. $zfile = $this->app->loadClass('zfile');
  213. if(is_file($backup))
  214. {
  215. $summary = array();
  216. $summary['allCount'] = 1;
  217. $summary['count'] = 1;
  218. $summary['size'] = $zfile->getFileSize($backup);
  219. return $summary;
  220. }
  221. $summaryFile = dirname($backup) . DS . 'summary';
  222. if(!file_exists($summaryFile)) return array();
  223. $summary = json_decode(file_get_contents(dirname($backup) . DS . 'summary'), true);
  224. return zget($summary, basename($backup), array());
  225. }
  226. /**
  227. * Get backup path.
  228. *
  229. * @access public
  230. * @return string
  231. */
  232. public function getBackupPath()
  233. {
  234. $backupPath = empty($this->config->backup->settingDir) ? $this->app->getTmpRoot() . 'backup' . DS : $this->config->backup->settingDir;
  235. return rtrim(str_replace(DS, '/', $backupPath), '/') . '/';
  236. }
  237. /**
  238. * Get backup file.
  239. *
  240. * @param string $name
  241. * @param string $type sql|file|code
  242. * @access public
  243. * @return string|false
  244. */
  245. public function getBackupFile($name, $type)
  246. {
  247. $backupPath = $this->getBackupPath();
  248. if($type == 'sql')
  249. {
  250. if(file_exists($backupPath . $name . ".{$type}")) return $backupPath . $name . ".{$type}";
  251. if(file_exists($backupPath . $name . ".{$type}.php")) return $backupPath . $name . ".{$type}.php";
  252. }
  253. if(file_exists($backupPath . $name . ".{$type}")) return $backupPath . $name . ".{$type}";
  254. return false;
  255. }
  256. /**
  257. * Get tmp log file.
  258. *
  259. * @param string $backupFile
  260. * @access public
  261. * @return string
  262. */
  263. public function getTmpLogFile($backupFile)
  264. {
  265. return $backupFile . '.tmp.summary';
  266. }
  267. /**
  268. * Get backup dir progress.
  269. *
  270. * @param string $backup
  271. * @access public
  272. * @return array
  273. */
  274. public function getBackupDirProgress($backup)
  275. {
  276. $tmpLogFile = $this->getTmpLogFile($backup);
  277. if(file_exists($tmpLogFile))
  278. {
  279. $log = json_decode(file_get_contents($tmpLogFile), true);
  280. return empty($log) ? array() : $log;
  281. }
  282. return array('allCount' => 0, 'count' => 0);
  283. }
  284. /**
  285. * Process filesize.
  286. *
  287. * @param int $fileSize
  288. * @access public
  289. * @return string
  290. */
  291. public function processFileSize($fileSize)
  292. {
  293. $bit = 'KB';
  294. $fileSize = round($fileSize / 1024, 2);
  295. if($fileSize >= 1024)
  296. {
  297. $bit = 'MB';
  298. $fileSize = round($fileSize / 1024, 2);
  299. }
  300. if($fileSize >= 1024)
  301. {
  302. $bit = 'GB';
  303. $fileSize = round($fileSize / 1024, 2);
  304. }
  305. return $fileSize . $bit;
  306. }
  307. /**
  308. * Process backup summary.
  309. *
  310. * @param string $file
  311. * @param int $count
  312. * @param int $size
  313. * @param array $errorFiles
  314. * @param int $allCount
  315. * @param string $action add|delete
  316. * @access public
  317. * @return bool
  318. */
  319. public function processSummary($file, $count, $size, $errorFiles = array(), $allCount = 0, $action = 'add')
  320. {
  321. $backupPath = dirname($file);
  322. $fileName = basename($file);
  323. $summaryFile = $backupPath . DS . 'summary';
  324. if(!file_exists($summaryFile) and !touch($summaryFile)) return false;
  325. $summary = json_decode(file_get_contents($summaryFile), true);
  326. if(empty($summary)) $summary = array();
  327. if($action == 'add')
  328. {
  329. $summary[$fileName]['allCount'] = $allCount;
  330. $summary[$fileName]['errorFiles'] = $errorFiles;
  331. $summary[$fileName]['count'] = $count;
  332. $summary[$fileName]['size'] = $size;
  333. }
  334. else
  335. {
  336. unset($summary[$fileName]);
  337. }
  338. if(file_put_contents($summaryFile, json_encode($summary))) return true;
  339. return false;
  340. }
  341. /**
  342. * Get disk space.
  343. *
  344. * @param string $backupPath
  345. * @access public
  346. * @return string
  347. */
  348. public function getDiskSpace($backupPath)
  349. {
  350. $nofile = strpos($this->config->backup->setting, 'nofile') !== false;
  351. $diskFreeSpace = disk_free_space($backupPath);
  352. $backFileSize = 0;
  353. if(!$nofile)
  354. {
  355. $appRoot = $this->app->getAppRoot();
  356. $appRootSize = $this->getDirSize($appRoot);
  357. $backFileSize = $appRootSize - $this->getDirSize($appRoot . 'tmp') - $this->getDirSize($appRoot . 'www/data/course');
  358. }
  359. $backSqlSize = $this->dao->select('sum(data_length+index_length) as size')
  360. ->from('information_schema.tables')
  361. ->where('TABLE_SCHEMA')->eq($this->config->db->name)
  362. ->groupBy('TABLE_SCHEMA')
  363. ->fetch('size');
  364. return $diskFreeSpace . ',' . ($backFileSize + $backSqlSize);
  365. }
  366. /**
  367. * Get directory size.
  368. *
  369. * @param string $dir
  370. * @access public
  371. * @return int
  372. */
  373. public function getDirSize($dir)
  374. {
  375. if(!file_exists($dir)) return 0;
  376. if(!is_readable($dir)) return 0;
  377. $totalSize = 0;
  378. $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::LEAVES_ONLY, RecursiveIteratorIterator::CATCH_GET_CHILD);
  379. foreach($iterator as $file) $totalSize += $file->getSize();
  380. return $totalSize;
  381. }
  382. }