PageRenderTime 34ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/core/components/databackup/model/mysql/dbbackup.class.php

https://github.com/jgulledge19/DataBackup
PHP | 570 lines | 342 code | 26 blank | 202 comment | 69 complexity | 68150d29edd24f4846c6f324cb5554d2 MD5 | raw file
  1. <?php
  2. /**
  3. *
  4. * Use this class to do a backup of your database
  5. * @author Raul Souza Silva (raul.3k@gmail.com)
  6. * @category Database
  7. * @copyright No one. You can copy, edit, do anything you want. If you change anything to better, please let me know.
  8. * Based From: http://www.phpclasses.org/browse/file/33388.html
  9. *
  10. */
  11. Class DBBackup {
  12. /**
  13. *
  14. * The host you will connect
  15. * @var String
  16. */
  17. protected $host;
  18. /**
  19. *
  20. * The driver you will use to connect
  21. * @var String
  22. */
  23. protected $driver;
  24. /**
  25. *
  26. * The user you will use to connect to a database
  27. * @var String
  28. */
  29. protected $user;
  30. /**
  31. *
  32. * The password you will use to connect to a database
  33. * @var String
  34. */
  35. protected $password;
  36. /**
  37. *
  38. * The database you will use to connect
  39. * @var String
  40. */
  41. protected $dbName;
  42. /**
  43. *
  44. * String to connect to the database using PDO
  45. * @var String
  46. */
  47. protected $dsn;
  48. /**
  49. *
  50. * Array with the tables of the database
  51. * @var Array
  52. */
  53. protected $tables = array();
  54. /**
  55. *
  56. * Hold the connection
  57. * @var ObjectConnection
  58. */
  59. protected $handler;
  60. /**
  61. *
  62. * Array to hold the errors
  63. * @var Array
  64. */
  65. protected $error = array();
  66. /**
  67. *
  68. * The result string. String with all queries
  69. * @var String
  70. */
  71. protected $final;
  72. /**
  73. *
  74. * Some config options
  75. * @var Array
  76. */
  77. protected $config = array();
  78. /**
  79. *
  80. * To include only these tables
  81. * @var Array
  82. */
  83. protected $includeTables = array();
  84. /**
  85. *
  86. * True include only these tables, false don't use include
  87. * @var boolean
  88. */
  89. protected $useIncludeTables = false;
  90. /**
  91. *
  92. * To exclude only these tables
  93. * @var Array
  94. */
  95. protected $excludeTables = array();
  96. /**
  97. *
  98. * True exclude only these tables, false don't use exclude
  99. * @var boolean
  100. */
  101. protected $useExcludeTables = false;
  102. /**
  103. *
  104. * List of files that are written, folder => path, database => path, tables => array( names => path)
  105. * @var Array
  106. */
  107. protected $filePathData = array();
  108. /**
  109. *
  110. * The main function
  111. * @method DBBackup
  112. * @uses Constructor
  113. * @param Array $args{host, driver, user, password, database}
  114. * @example $db = new DBBackup(array('host'=>'my_host', 'driver'=>'bd_type(mysql)', 'user'=>'db_user', 'password'=>'db_password', 'database'=>'db_name'));
  115. */
  116. public function __construct(&$modx, $config=array()){
  117. $this->modx = &$modx;
  118. $this->handler = $this->modx->pdo;
  119. $defaults = array(
  120. 'comment_prefix' => '-- ',
  121. 'comment_suffix' => '',
  122. 'new_line' => "\n",
  123. 'base_path' => MODX_CORE_PATH.'components/databackup/dumps/',
  124. 'temp_path' => MODX_CORE_PATH.'components/databackup/dumps/tmp/',
  125. 'write_file' => true,
  126. 'write_table_files' => true,
  127. 'use_drop' => true,
  128. 'connect' => false,
  129. 'database' => $modx->getOption('dbname'),
  130. 'create_database' => false,
  131. 'includeTables' => null,
  132. 'excludeTables' => null
  133. );
  134. $this->config = array_merge( $defaults, $config );
  135. if ( isset($this->config['connect']) && $this->config['connect'] ) {
  136. echo 'Connect ';
  137. if( empty($this->config['host'])) {
  138. $this->error[] = 'Parameter host missing';
  139. }
  140. if( empty($this->config['database'])) {
  141. $this->error[] = 'Parameter database missing';
  142. }
  143. if(empty($this->config['driver'])) {
  144. $this->error[] = 'Parameter driver missing';
  145. }
  146. //if(!$this->config['user']) $this->error[] = 'Parameter user missing';
  147. //if(!isset($this->config['password'])) $this->error[] = 'Parameter password missing';
  148. if(count($this->error)>0){
  149. return;
  150. }
  151. $this->host = $this->config['host'];
  152. $this->driver = $this->config['driver'];
  153. $this->user = $this->config['user'];
  154. $this->password = $this->config['password'];
  155. $this->dbName = $this->config['database'];
  156. if ( $this->host=='localhost' ) {
  157. // We have a little issue in unix systems when you set the host as localhost
  158. $this->host = '127.0.0.1';
  159. }
  160. $this->dsn = $this->driver.':host='.$this->host.';dbname='.$this->dbName;
  161. $this->_connect();
  162. } else {
  163. $this->dbName = $this->config['database'];
  164. }
  165. // set the include/exclude if any
  166. if ( !empty($this->config['includeTables']) ) {
  167. $in = explode(',',$this->config['includeTables']);
  168. // remove white space
  169. foreach ( $in as $table ) {
  170. $this->includeTables[] = trim($table);
  171. }
  172. $this->useIncludeTables = true;
  173. } elseif ( !empty($this->config['excludeTables']) ) {
  174. $ex = explode(',',$this->config['excludeTables']);
  175. // remove white space
  176. foreach ( $ex as $table ) {
  177. $this->excludeTables[] = trim($table);
  178. }
  179. $this->useExcludeTables = true;
  180. }
  181. }
  182. /**
  183. *
  184. * Call this function to get the database backup
  185. * @example DBBackup::backup();
  186. */
  187. public function backup() {
  188. if ( count($this->error) > 0 ){
  189. //echo '<br>Error - backup: '.$this->dbName.' L:'.__LINE__;
  190. return false;
  191. }
  192. //echo '<br>Database: '.$this->dbName.' L: '.__LINE__;
  193. $this->_getTables();
  194. if ( !is_dir($this->config['temp_path']) ) {
  195. mkdir($this->config['temp_path']);
  196. }
  197. $this->_generate();
  198. // clean up temp:
  199. $this->purge(0, 'temp_path');
  200. //return $this->final;
  201. if ( count($this->error)>0 ) {
  202. return false;//, 'msg'=>$this->error);
  203. }
  204. return true;
  205. }
  206. /**
  207. * @description returns the folder/directory path that was created on for the backup files
  208. * @return string
  209. */
  210. public function folderPath() {
  211. if ( isset($this->filePathData['folder'])) {
  212. return $this->filePathData['folder'];
  213. }
  214. return null;
  215. }
  216. /**
  217. * @description returns the database file path that was created on for the backup
  218. * @return string
  219. */
  220. public function DBFilePath() {
  221. if ( isset($this->filePathData['database'])) {
  222. return $this->filePathData['database'];
  223. }
  224. return null;
  225. }
  226. /**
  227. * @description returns the database table file path that was created on for the backup
  228. * @param (string) the full table name
  229. * @return string
  230. */
  231. public function tableFilePath($table) {
  232. if ( isset($this->filePathData['tables'][$table])) {
  233. return $this->filePathData['tables'][$table];
  234. }
  235. return null;
  236. }
  237. /**
  238. * Get the errors
  239. * @return string
  240. */
  241. public function getErrors(){
  242. return implode(', ', $this->error);
  243. }
  244. /**
  245. * Purge file records
  246. * @param (INT) $seconds
  247. * @param (String) $path_name
  248. * @return void
  249. */
  250. public function purge($seconds=1814400, $path_name='base_path'){// 21 days is the default
  251. // purge data older then 3 weeks:
  252. $data_folder = $this->config[$path_name];
  253. $path = dirname($data_folder.'/file.txt');
  254. $windows_path = str_replace('\\', '/', $path);
  255. $core = dirname(MODX_CORE_PATH.'/file.txt');
  256. $windows_core = str_replace('\\', '/', $core);
  257. $manager = dirname(MODX_MANAGER_PATH.'/file.txt');
  258. $windows_manager = str_replace('\\', '/', $manager);
  259. $assets = dirname(MODX_ASSETS_PATH.'/file.txt');
  260. $windows_assets = str_replace('\\', '/', $assets);
  261. // do not allow in the core path
  262. if ( $path == $core || $windows_core == $windows_path || strpos($core, $path) !== false || strpos($windows_core, $windows_path) !== false || $path == '' ) {
  263. $this->modx->log(xPDO::LOG_LEVEL_ERROR, '[DBbackup] ERROR cannot purge: '.$data_folder.' it is with in the core path: '.MODX_CORE_PATH);
  264. return false;
  265. } elseif ( $path == $manager || $windows_manager == $windows_path || strpos($manager, $path) !== false || strpos($windows_manager, $windows_path) !== false ) {
  266. $this->modx->log(xPDO::LOG_LEVEL_ERROR, '[DBbackup] ERROR cannot purge: '.$data_folder.' it is with in the manager path: '.MODX_MANAGER_PATH);
  267. return false;
  268. } elseif ( $path == $assets || $windows_assets == $windows_path || strpos($assets, $path) !== false || strpos($windows_assets, $windows_path) !== false ) {
  269. $this->modx->log(xPDO::LOG_LEVEL_ERROR, '[DBbackup] ERROR cannot purge: '.$data_folder.' it is with in the assets path: '.MODX_ASSETS_PATH);
  270. return false;
  271. }
  272. $open_dir = opendir( $data_folder ) ;
  273. $last_date = time() - $seconds;// 3600*24*21;// 21 days
  274. while ( $tmp_file = readdir( $open_dir ) ) {
  275. if ( $tmp_file != '.' && $tmp_file != '..' ) {
  276. # dir
  277. //echo '<br>Folder: '.$tmp_file;
  278. if ( is_dir( $data_folder.$tmp_file ) ) {
  279. $stats = lstat($data_folder.$tmp_file);
  280. if ($stats['ctime'] < $last_date ) {
  281. // delete old files
  282. //echo ' - DELETE';
  283. $this->_rmdir_files($data_folder.$tmp_file.'/');
  284. }
  285. }
  286. # else files
  287. else if ( $path_name == 'temp_path' && is_file($data_folder.$tmp_file) ) {
  288. unlink($data_folder.$tmp_file);
  289. }
  290. }
  291. }
  292. closedir($open_dir);
  293. }
  294. /**
  295. * Deletes directory files
  296. *
  297. */
  298. protected function _rmdir_files($dir) {
  299. foreach( glob( $dir . '*', GLOB_MARK ) as $file) {
  300. //$open_dir = opendir( $dir ) ;
  301. //while ( $file = readdir( $open_dir ) ) {
  302. if (is_dir($file)) {
  303. $this->_rmdir_files($file."/");
  304. rmdir($file);
  305. } elseif( is_file($file) ) {
  306. //echo '<br> Unlink file: '.$file;
  307. unlink($file);
  308. }
  309. }
  310. //closedir($dir);
  311. if (is_dir($dir) ){
  312. if( rmdir( $dir ) ){
  313. return true;
  314. }
  315. return false;
  316. }
  317. }
  318. /**
  319. *
  320. * Connect to a database
  321. * @uses Private use
  322. */
  323. protected function _connect(){
  324. try {
  325. if ( !empty($this->user) ){
  326. $this->handler = new PDO($this->dsn, $this->user, $this->password);
  327. } else {
  328. $this->handler = new PDO($this->dsn);
  329. }
  330. } catch (PDOException $e) {
  331. $this->handler = null;
  332. //echo '<br>PDO not connected: '.$e->getMessage();
  333. $this->error[] = $e->getMessage();
  334. return false;
  335. }
  336. }
  337. /**
  338. *
  339. * Generate backup string
  340. * @uses Private use
  341. */
  342. protected function _generate(){
  343. if ( $this->config['create_database'] ) {
  344. $this->final = $this->config['comment_prefix'].'CREATING DATABASE '.$this->dbName.' '.$this->config['comment_suffix'].$this->config['new_line'];
  345. $this->final .= 'CREATE DATABASE ' . $this->dbName.";".$this->config['new_line'];
  346. $this->final .= 'USE ' . $this->dbName.';'.$this->config['new_line'].$this->config['new_line'];
  347. } else {
  348. $this->final = $this->config['comment_prefix'].'RESTORING TABLES '.$this->dbName.' '.$this->config['comment_suffix'].$this->config['new_line'];
  349. }
  350. // create base folder - DB_backup_time()
  351. if ( $this->config['write_file'] || $this->config['write_table_files'] ) {
  352. $dir = $this->config['base_path'].''.$this->dbName.'_'.date('Y_m_d').'__'.time().'/';
  353. $this->filePathData['folder'] = $dir;
  354. if( !is_dir($dir) ){
  355. mkdir($dir);
  356. }
  357. }
  358. // start the large SQL dump file:
  359. if ( $this->config['write_file'] ) {
  360. file_put_contents($dir.'complete_db_backup.sql', $this->final );
  361. $this->filePathData['database'] = $dir.'complete_db_backup.sql';
  362. }
  363. foreach ($this->tables as $tbl) {
  364. $table_sql = $this->config['comment_prefix'].'CREATING TABLE '.$tbl['name'].$this->config['comment_suffix'].$this->config['new_line'];
  365. $table_sql .= $tbl['create'] . ";".$this->config['new_line'].$this->config['new_line'];
  366. $table_sql .= $this->config['comment_prefix'].'INSERTING DATA INTO '.$tbl['name'].$this->config['comment_suffix'].$this->config['new_line'];
  367. //$table_sql .= $this->_getData($tbl['name']).$this->config['new_line'].$this->config['new_line'].$this->config['new_line'];
  368. $file = $this->_getData($tbl['name']).$this->config['new_line'].$this->config['new_line'].$this->config['new_line'];
  369. $data = file_get_contents(trim($file));
  370. // $this->final .= $table_sql;// 1.1.6
  371. // write table to file
  372. if ( $this->config['write_table_files'] ) {
  373. file_put_contents($dir.$tbl['name'].'.sql', $table_sql );
  374. // copy file into file:
  375. file_put_contents(
  376. $dir.$tbl['name'].'.sql',
  377. $data,
  378. FILE_APPEND
  379. );
  380. $this->filePathData['tables'][$tbl['name']] = $dir.$tbl['name'].'.sql';
  381. }
  382. // added 1.1.6:
  383. if ( $this->config['write_file'] ) {
  384. file_put_contents(
  385. $dir.'complete_db_backup.sql',
  386. $table_sql,
  387. FILE_APPEND
  388. );
  389. // copy file into file:
  390. file_put_contents(
  391. $dir.'complete_db_backup.sql',
  392. $data,
  393. FILE_APPEND
  394. );
  395. }
  396. // reset memory?
  397. $table_sql = null;
  398. unset($tbl);
  399. unset($data);
  400. }
  401. //$this->final .= $this->config['comment_prefix'].' THE END'.$this->config['new_line'].$this->config['comment_suffix'].$this->config['new_line'];
  402. if ( $this->config['write_file'] ) {
  403. file_put_contents(
  404. $dir.'complete_db_backup.sql',
  405. $this->config['comment_prefix'].' THE END'.$this->config['new_line'].$this->config['comment_suffix'].$this->config['new_line'],
  406. FILE_APPEND
  407. );
  408. $this->filePathData['database'] = $dir.'complete_db_backup.sql';
  409. }
  410. }
  411. /**
  412. *
  413. * Get the list of tables
  414. * @uses Private use
  415. */
  416. protected function _getTables(){
  417. try {
  418. $stmt = $this->handler->query('SHOW TABLES');
  419. $tbs = $stmt->fetchAll(PDO::FETCH_NUM);
  420. $i=0;
  421. foreach($tbs as $table){
  422. //echo '<br>Table: '. $table[0];
  423. if ( $this->useIncludeTables ) {
  424. //echo ' - useIncludes';
  425. if ( !in_array($table[0],$this->includeTables)) {
  426. //echo ' - exclude me';
  427. continue;
  428. }
  429. } elseif ( $this->useExcludeTables ) {
  430. if ( in_array($table[0],$this->excludeTables)) {
  431. continue;
  432. }
  433. }
  434. $this->tables[$i]['name'] = $table[0];
  435. $this->tables[$i]['create'] = $this->_getColumns($table[0]);
  436. //$this->tables[$i]['data'] = $this->_getData($table[0]);
  437. $i++;
  438. }
  439. unset($stmt);
  440. unset($tbs);
  441. unset($i);
  442. return true;
  443. } catch (PDOException $e) {
  444. $this->handler = null;
  445. $this->error[] = $e->getMessage();
  446. return false;
  447. }
  448. }
  449. /**
  450. *
  451. * Get the list of Columns
  452. * @uses Private use
  453. */
  454. protected function _getColumns($tableName){
  455. // also see: http://www.sitepoint.com/forums/php-application-design-147/pdo-getcolumnmeta-bug-497257.html#post3510380
  456. try {
  457. $sql = '';
  458. if ( $this->config['use_drop']) {
  459. $sql = 'DROP TABLE IF EXISTS `'.$tableName.'`;'.$this->config['new_line'].$this->config['new_line'];
  460. }
  461. $stmt = $this->handler->query('SHOW CREATE TABLE '.$tableName);
  462. $q = $stmt->fetchAll();
  463. // reset the auto increment?
  464. $sql .= preg_replace("/AUTO_INCREMENT=[\w]*./", '', $q[0][1]);
  465. return $sql;
  466. } catch (PDOException $e){
  467. $this->handler = null;
  468. $this->error[] = $e->getMessage();
  469. return false;
  470. }
  471. }
  472. /**
  473. *
  474. * Get the insert data of tables
  475. * @uses Private use
  476. */
  477. protected function _getData($tableName){
  478. try {
  479. $stmt = $this->handler->query('SELECT * FROM '.$tableName);
  480. $q = $stmt->fetchAll(PDO::FETCH_NUM);
  481. // echo 'Table: '.$tableName; print_r($q);
  482. $data = '';
  483. $file = $this->config['temp_path'].$tableName.'.tmp';
  484. // create empty file: this will purge any system that is caching files
  485. file_put_contents(
  486. $file,
  487. '',
  488. LOCK_EX
  489. );
  490. $count = 0;
  491. $data = array();
  492. foreach ($q as $pieces){
  493. $data[$count] = 'INSERT INTO `'. $tableName .'` VALUES ( ';//.' (\'' . implode('\',\'', $pieces) . '\');'.$this->config['new_line'];
  494. $str = '';
  495. foreach($pieces as $value){
  496. // &acirc;€™
  497. //$value = htmlentities(addslashes($value));
  498. if ( !empty($str) ){
  499. $str .= ', ';
  500. }
  501. if ( is_null($value) ) {
  502. $str .= 'NULL';
  503. } else {
  504. $str .= '\''.addslashes($value).'\'';
  505. }
  506. }
  507. $data[$count] .= $str.');'.$this->config['new_line'];
  508. $count++;
  509. // create temp file:
  510. if ( $count >= 1000 ){
  511. file_put_contents(
  512. $file,
  513. implode('', $data),
  514. FILE_APPEND
  515. );
  516. // reset count and data str:
  517. $count = 0;
  518. $data = array();
  519. }
  520. //$data .= 'INSERT INTO `'. $tableName .'` VALUES '.( is_null($value)).' (\'' . implode('\',\'', $pieces) . '\');'.$this->config['new_line'];
  521. }
  522. if ( $count > 0 ){
  523. file_put_contents(
  524. $file,
  525. implode('', $data),
  526. FILE_APPEND
  527. );
  528. }
  529. if ( $tableName == 'modx_access_context' ) {
  530. //echo 'Table: modx_access_context';
  531. //exit();
  532. }
  533. unset($stmt);
  534. unset($q);
  535. //gc_collect_cycles();// requires php 5.3+
  536. return $file;
  537. } catch (PDOException $e){
  538. $this->handler = null;
  539. $this->error[] = $e->getMessage();
  540. return false;
  541. }
  542. }
  543. }