YaWK  24.1
Yet another WebKit
backup-mysqlBackup.php
Go to the documentation of this file.
1 <?php
3 {
4 
5  use Exception;
6 
7  /**
8  * @details <b>YaWK Backup Component: Database Class</b>
9  *
10  * <p>Methods to backup and restore all or partial data from mysql database.
11  * This class makes use of ifsnop\mysqldump-php by diego torres. His work make
12  * possible to ensure that this backup component works with all typical webhosting
13  * configurations. (Especially those who do not allow shell or forbid the use of
14  * mysqldump.) This class should work in mostly any situation.
15  * Thank you, Diego Torres!</p>
16  *
17  * @package YAWK
18  * @author Daniel Retzl <[email protected]>
19  * @copyright 2018 Daniel Retzl
20  * @license https://opensource.org/licenses/MIT
21  * @version 1.0.0
22  * @brief This class serves methods to store and retrieve mysql database.
23  */
24  class mysqlBackup extends \YAWK\BACKUP\backup
25  {
26  /** @param object mysqldump object */
27  public $mysqldump;
28  /** @param object zip object */
29  public $zip;
30  /** @param array mysql config array */
31  private $config;
32  /** @param string mysql server hostname */
33  private $host;
34  /** @param string mysql database name */
35  private $dbname;
36  /** @param string mysql user */
37  private $user;
38  /** @param string mysql pass */
39  private $pass;
40  /** @param string mysql prefix */
41  private $prefix;
42  /** @param string mysql port */
43  private $port;
44  /** @param string backup mode (include|exclude|all) */
45  public $backupMode = 'include';
46  /** @param array exclude tables */
47  public $excludeTablesArray = array();
48  /** @param array include tables */
49  public $includeTablesArray = array();
50  /** @param array mysqldump settings */
51  public $dumpSettings = array();
52  /** @param string path, where the backup will be stored */
53  public $sqlPath = '../system/backup/current/';
54  /** @param string default filename of mysqldump .sql file */
55  public $backupSqlFile = 'database-backup.sql';
56  /** @param string name of the backup .sql file */
57  public $sqlBackup = '';
58  /** @param string hash value of .sql file */
59  public $hashValue = '';
60 
61 
62  /**
63  * @brief Initialize this database backup class
64  * @param object $db database object
65  * @param string $overwriteBackup if overwrite backup is allowed or not "true" | "false"
66  * @param string $zipBackup if backup should be zipped or not "true" | "false"
67  * @param string $storeSqlTmp if backup should be stored in tmp/database "true" | "false"
68  * @return bool
69  */
71  {
72  // if overwrite backup is true
73  $this->overwriteBackup = $overwriteBackup;
74  // zip true|false
75  $this->zipBackup = $zipBackup;
76  // store .sql in tmp folder true|false
77  $this->storeSqlTmp = $storeSqlTmp;
78 
79  // check if .sql file should be stored in tmp folder
80  if (isset($this->storeSqlTmp) && ($this->storeSqlTmp == "true"))
81  { // if so, do not zip
82  $this->zipBackup = "false";
83  }
84 
85  // start database backup
86  if ($this->startMysqlBackup($db) === true)
87  { // mysql backup done
88  return true;
89  }
90  else
91  { // mysql backup failed
92  return false;
93  }
94  }
95 
96  /**
97  * @brief Set mysqldump settings
98  * @return array $this->dumpSettings
99  * @details mysqldump settings eg. include or exclude tables from database
100  */
101  public function setDumpSettings()
102  {
103  // generate dump settings array
104  $this->dumpSettings = array(
105  'include-tables'
106  => array(),
107  'exclude-tables'
108  => array());
109  return $this->dumpSettings;
110  }
111 
112  /**
113  * @brief Exclude tables from backup
114  * @param array $excludeTables array
115  * @return array $this->dumpSettings
116  * @details awaits an array with all tables that should be excluded
117  */
118  public function excludeTables($excludeTables)
119  { // check if exclude tables are set
120  if (isset($excludeTables) && (is_array($excludeTables) && (!empty($excludeTables))))
121  {
122  // set excludeTables array
123  $this->excludeTablesArray = $excludeTables;
124 
125  // walk through exclude tables array
126  foreach ($this->excludeTablesArray AS $exclude => $table)
127  { // add exclude table to array
128  $this->dumpSettings['exclude-tables'][] = $table;
129  }
130  }
131 
132  // check if dump settings array is set and not empty
133  if (isset($this->dumpSettings['exclude-tables']) && (is_array($this->dumpSettings['exclude-tables']) && (!empty($this->dumpSettings['exclude-tables']))))
134  { // return dump settings array
135  return $this->dumpSettings;
136  }
137  else
138  { // no tables set for inclusion - return empty array
139  return $this->dumpSettings['exclude-tables'] = array();
140  }
141  }
142 
143  /**
144  * @brief Include only this tables into backup
145  * @param array $includeTables array
146  * @return array $this->dumpSettings
147  * @details awaits an array with all tables that should be included
148  */
149  public function includeTables($includeTables)
150  { // check if include tables are set
151  if (isset($includeTables) && (is_array($includeTables) && (!empty($includeTables))))
152  {
153  // set includeTables array
154  $this->includeTablesArray = $includeTables;
155 
156  // walk through include tables array
157  foreach ($this->includeTablesArray AS $include => $table)
158  { // add include table to array
159  $this->dumpSettings['include-tables'][] = $table;
160  }
161  }
162 
163  // check if dump settings array is set and not empty
164  if (isset($this->dumpSettings['include-tables']) && (is_array($this->dumpSettings['include-tables']) && (!empty($this->dumpSettings['include-tables']))))
165  { // return dump settings array
166  return $this->dumpSettings;
167  }
168  else
169  { // no tables set for inclusion - return empty array
170  return $this->dumpSettings['include-tables'] = array();
171  }
172  }
173 
174  /**
175  * @brief Get current database config
176  * @details Get mysql configuration and set settings as private properties
177  */
178  public function getDatabaseConfig()
179  {
180  // get database configuration
181  require ("../system/classes/dbconfig.php");
182  // set configuration vars for mysqldump-php
183  $this->host = $this->config['server'];
184  $this->user = $this->config['username'];
185  $this->pass = $this->config['password'];
186  $this->dbname = $this->config['dbname'];
187  $this->prefix = $this->config['prefix'];
188  $this->port = $this->config['port'];
189  }
190 
191  /**
192  * @brief Include mysqldump-php and create new dump object
193  * @return bool true|false
194  * @throws Exception
195  * @link http://yawk.io
196  * @details create new $this->mysqldump object
197  * @author Daniel Retzl <[email protected]>
198  * @version 1.0.0
199  */
200  public function includeMysqldumpClass($db)
201  {
202  // check if mysqldump class is there
203  if (is_file('../system/engines/mysqldump/Mysqldump.php'))
204  {
205  // ok, include class
206  require_once('../system/engines/mysqldump/Mysqldump.php');
207 
208  // check if database tables form is set
209  if (isset($_POST['database']) && (!empty($_POST['database'])))
210  {
211  // walk through database tables array
212  foreach ($_POST['database'] as $table)
213  {
214  // store table in array
215  $this->includeTablesArray[] = $table;
216  }
217 
218  // set tables to include
219  $this->dumpSettings = $this->includeTables($this->includeTablesArray);
220  }
221  else
222  { // set default settings (dump all)
223  $this->setDumpSettings();
224  }
225 
226  // load database config into this object
227  $this->getDatabaseConfig();
228 
229  // create new mysqldump object
230  $this->mysqldump = new \Ifsnop\Mysqldump\Mysqldump("mysql:host=$this->host;dbname=$this->dbname", $this->user, $this->pass, $this->dumpSettings);
231  return true;
232  }
233  else
234  { // unable to include class
235  \YAWK\sys::setSyslog($db, 52, 2, "unable to backup: failed to include mysqldump class", 0, 0, 0, 0);
236  return false;
237  }
238  }
239 
240  /**
241  * @brief Check if .sql backup file exists
242  * @details return bool if $this->sqlBackup exists
243  * @return bool true|false
244  */
245  public function sqlFileExists()
246  {
247  // set path + filename (store as string in $this->sqlBackup)
248  $this->sqlBackup = $this->sqlPath.$this->backupSqlFile;// .sql backup file exists
249  // check if .sql backup file is already there
250  if (is_file($this->sqlBackup))
251  {
252  // build sqlBackup string
253  return true;
254  }
255  else
256  {
257  // .sql backup file does not exist
258  return false;
259  }
260  }
261 
262  /**
263  * @brief Write backup.ini file (used by backup restore methods)
264  * @details write all relevant backup information into this file
265  * @return array $this->backupSettings
266  */
267  public function setBackupSettings($db)
268  {
269  /** @var $db \YAWK\db */
270  // set some backup info variables
271  $this->backupSettings['DATE'] = \YAWK\sys::now();
272  $this->backupSettings['METHOD'] = $this->backupMethod;
273  $this->backupSettings['FILE'] = $this->backupSqlFile;
274  $this->backupSettings['HASH'] = $this->getHashValue($db);
275  $this->backupSettings['PATH'] = $this->sqlPath;
276  $this->backupSettings['SOURCE_FOLDER'] = $this->sqlBackup;
277  $this->backupSettings['OVERWRITE_ALLOWED'] = $this->overwriteBackup;
278  $this->backupSettings['USER_ID'] = $_SESSION['uid'];
279  $this->backupSettings['TABLES'] = '';
280  foreach ($this->dumpSettings['include-tables'] as $table)
281  {
282  $this->backupSettings['TABLES'] .= $table.",";
283  }
284  return $this->backupSettings;
285  }
286 
287  /**
288  * @brief get hash value from .sql backup file
289  * @param $db
290  * @return bool|string
291  */
292  public function getHashValue($db)
293  {
294  // check if sql backup file is accessable
295  if (is_file($this->sqlBackup))
296  { // generate hash value
297  return $this->hashValue = hash_file('md5', $this->sqlBackup);
298  }
299  else
300  { // sql backup file not found
301  // unable to generate hash value
302  \YAWK\sys::setSyslog($db, 52, 2, "failed to generate hash value - $this->sqlBackup not accessable", 0, 0, 0, 0);
303  return false;
304  }
305  }
306 
307  /**
308  * @brief Start mysqldump and check if .sql file exists. Zip it afterwards if enabled.
309  * @details return bool if $this->sqlBackup exists
310  * @return bool true|false
311  */
312  public function doSqlBackup($db)
313  {
314 
315  // check if .sql file should be stored in tmp folder...
316  if (isset($this->storeSqlTmp) && ($this->storeSqlTmp == "true"))
317  { // check if subdir database exists
318  if (is_writeable($this->tmpFolder))
319  { // check if tmp subdir (database) exists...
320  if (!is_dir($this->tmpFolder."database"))
321  { // if not, create it
322  mkdir($this->tmpFolder."database");
323  }
324  // set sql path to temp folder
325  $this->sqlPath = $this->tmpFolder."database/";
326  // set whole sql backup file path + filename
327  $this->sqlBackup = $this->sqlPath.$this->backupSqlFile;
328  }
329  else
330  {
331  \YAWK\sys::setSyslog($db, 51, 1, "failed to create database backup: $this->tmpFolder is not writeable", 0, 0, 0, 0);
332  return false;
333  }
334  }
335 
336  // check if .sql path is writeable
337  if (is_writeable($this->sqlPath))
338  {
339  // ok then...
340  try
341  { // try to start backup
342  $this->mysqldump->start($this->sqlBackup, $this->dumpSettings);
343  }
344  // on fail: catch error
345  catch (Exception $e)
346  {
347  // output mysqldump error
348  \YAWK\sys::setSyslog($db, 52, 2, "".$e->getMessage()."", 0, 0, 0, 0);
349  // echo 'mysqldump-php error: ' . $e->getMessage();
350  }
351 
352  // check if file exists
353  if ($this->sqlFileExists())
354  {
355  // ok, backup done
356  // add ini file
357  $this->backupSettings = $this->setBackupSettings($db);
358  if ($this->setIniFile($db) === true)
359  {
360  // backup ini file written
361  }
362  else
363  {
364  // set syslog entry: ini file not written
365  \YAWK\sys::setSyslog($db, 51, 1, "failed to write $this->configFile", 0, 0, 0, 0);
366  }
367 
368  // check if .sql file should be zipped
369  if ($this->zipBackup == "true")
370  {
371  // generate zip archive
372  if ($this->generateZipArchive($db, $this->sqlBackup) === true)
373  {
374  // zip archive created
375  \YAWK\sys::setSyslog($db, 49, 0, "created database backup $this->sqlBackup", 0, 0, 0, 0);
376  return true;
377  }
378  else
379  { // failed to create zip archive
380  \YAWK\sys::setSyslog($db, 51, 1, "failed to create database zip archive", 0, 0, 0, 0);
381  return false;
382  }
383  }
384  else
385  { // zip backup diabled, no zip needed
386  return true;
387  }
388  }
389  else
390  { // .sql file does not exist - backup failed?
391  return false;
392  }
393  }
394  else
395  {
396  \YAWK\sys::setSyslog($db, 51, 1, "failed to create database backup: $this->sqlPath is not writeable", 0, 0, 0, 0);
397  return false;
398  }
399  }
400 
401  /**
402  * @brief ZIP Archive method generates a zip archive from .sql file
403  * @param string $sqlBackup relative path + filename to the .sql backup file
404  * @details zip the .sql file and return bool if zip archive exists
405  * @return bool true|false
406  */
407  public function generateZipArchive($db, $sqlBackup)
408  {
409  if ($this->zipBackup == "true")
410  { // check if ZipArchive class is available
411  if ($this->checkZipFunction() == true)
412  {
413  // ok, create new zip object
414  $zip = new \ZipArchive();
415 
416  // get sql backup filename
417  if (isset($sqlBackup) && (!empty($sqlBackup) && (is_string($sqlBackup))))
418  { // set this obj property
419  $this->sqlBackup = $sqlBackup;
420  }
421 
422  // generate zip filename
423  $filename = $this->sqlBackup.".zip";
424 
425  // open new zip archive
426  if ($zip->open($filename, \ZipArchive::CREATE) !== TRUE)
427  { // failed to create new zip archive
428  \YAWK\sys::setSyslog($db, 51, 2, "failed to create new zip archive $filename", 0, 0, 0, 0);
429  return false;
430  }
431 
432  // add .sql file to zip archive
433  $zip->addFile($this->sqlBackup,$this->backupSqlFile);
434 
435  // check if there is a backup.ini / config file
436  if (is_file($this->sqlPath.$this->configFilename))
437  { // add backup.ini config file to zip archive
438  $zip->addFile($this->sqlPath.$this->configFilename, $this->configFilename);
439  }
440  else
441  { // backup config ini file not found
442  \YAWK\sys::setSyslog($db, 51, 2, "failed to add $this->configFilename to archive $filename", 0, 0, 0, 0);
443  }
444  // echo "numfiles: " . $zip->numFiles . "\n";
445  // echo "status:" . $zip->status . "\n";
446  // ok, close zip file
447  $zip->close();
448 
449  // check if zip file exists
450  if (is_file($filename))
451  {
452  // zip file created
453  if ($this->removeAfterZip == "true")
454  { // remove files
455  if (unlink ($this->sqlPath.$this->backupSqlFile)
456  && (unlink ($this->sqlPath.$this->configFilename)))
457  {
458  return true;
459  }
460  else
461  { // unable to delete backup files after zipping
462  \YAWK\sys::setSyslog($db, 51, 1, "failed to delete backup files after adding .zip archive $filename", 0, 0, 0, 0);
463  return true;
464  }
465  }
466  return true;
467  }
468  else
469  { // zip file not created
470  \YAWK\sys::setSyslog($db, 51, 1, "failed to create .zip file: $filename", 0, 0, 0, 0);
471  return false;
472  }
473  }
474  else
475  { // ZipArchive class not available
476  \YAWK\sys::setSyslog($db, 51, 1, "failed create .zip archive - PHP ZIP class not available. Ask your web hosting provider about that", 0, 0, 0, 0);
477  return false;
478  }
479  }
480  else
481  { // zip not enabled due settings
482  return false;
483  }
484  }
485 
486  /**
487  * @brief Start and manage mysql backup routine.
488  * <p>First of all, mysqldump class will be included. Then, a check runs if a .sql backup file exists.
489  * if so, check if overwrite backup is allowed. If this is true, doSqlBackup method will be called.
490  * (This function does the real job).</p>
491  * @details return bool if zip archive exists
492  * @return bool true|false
493  */
494  public function startMysqlBackup($db)
495  {
496  // include mysqldump class
497  $this->includeMysqldumpClass($db);
498 
499  // check if backup overwrite is allowed
500  if ($this->overwriteBackup == "false")
501  {
502  if (isset($_POST))
503  {
504  // check if new folder was entered by user
505  if (isset($_POST['newFolder']) && (!empty($_POST['newFolder'])))
506  {
507  // create new archive sub folder path
508  $this->archiveBackupSubFolder = $this->archiveBackupFolder.$_POST['newFolder']."/";
509 
510  // create new directory in archive
511  if (!is_dir($this->archiveBackupSubFolder))
512  {
513  if (mkdir($this->archiveBackupSubFolder))
514  { // all good, new archive subfolder created
515  // set syslog entry: dir created
516  \YAWK\sys::setSyslog($db, 49, 0, "archive directory created: $this->archiveBackupSubFolder", 0, 0, 0, 0);
517  }
518  else
519  { // failed to create new archive subfolder
520  // set syslog entry: failed
521  \YAWK\sys::setSyslog($db, 52, 0, "failed to create archive directory: $this->archiveBackupSubFolder", 0, 0, 0, 0);
522  }
523  }
524  }
525  // check if existing folder was selected by user
526  else if (isset($_POST['selectFolder']) && (!empty($_POST['selectFolder'])))
527  { // set archive sub foder path
528  $this->archiveBackupSubFolder = $this->archiveBackupFolder.$_POST['selectFolder']."/";
529  }
530 
531  // SET PATH WHERE .SQL FILE SHOULD BE STORED
532  $this->sqlPath = $this->archiveBackupSubFolder;
533  // set archive backup subfolder
534  $this->targetFolder = $this->archiveBackupSubFolder;
535  }
536  }
537 
538  // check if a backup exists
539  if ($this->sqlFileExists() === true)
540  {
541  // do database backup
542  if ($this->doSqlBackup($db) === true)
543  { // ok, backup done
544  \YAWK\sys::setSyslog($db, 50, 3, "database backup overwritten", 0, 0, 0, 0);
545  return true;
546  }
547  else
548  { // backup failed - unable to overwrite - check chmod settings!
549  \YAWK\sys::setSyslog($db, 52, 2, "failed to overwrite database backup", 0, 0, 0, 0);
550  return false;
551  }
552  }
553  else
554  { // .sql file does not exist - do database backup
555  if ($this->doSqlBackup($db) === true)
556  { // ok, backup done!
557  // \YAWK\sys::setSyslog($db, 50, 3, "created database backup", 0, 0, 0, 0);
558  return true;
559  }
560  else
561  { // backup failed!
562  \YAWK\sys::setSyslog($db, 52, 2, "failed to write database backup", 0, 0, 0, 0);
563  return false;
564  }
565  }
566  }
567  }
568 }
$filename
Definition: actions.php:10
generateZipArchive($db, $sqlBackup)
ZIP Archive method generates a zip archive from .sql file.
getHashValue($db)
get hash value from .sql backup file
setDumpSettings()
Set mysqldump settings.
includeMysqldumpClass($db)
Include mysqldump-php and create new dump object.
includeTables($includeTables)
Include only this tables into backup.
getDatabaseConfig()
Get current database config.
excludeTables($excludeTables)
Exclude tables from backup.
doSqlBackup($db)
Start mysqldump and check if .sql file exists. Zip it afterwards if enabled.
initMysqlBackup($db, $overwriteBackup, $zipBackup, $storeSqlTmp)
Initialize this database backup class.
sqlFileExists()
Check if .sql backup file exists.
startMysqlBackup($db)
Start and manage mysql backup routine.
setIniFile(object $db)
Set backup information file (backup.ini)
Definition: backup.php:254
checkZipFunction()
Check if ZipArchive function exists.
Definition: backup.php:423
static now()
returns the current datetime
Definition: sys.php:1492
The default user class. Provide all functions to handle the user object.
Definition: user.php:17