ZipBackup.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. <?php
  2. /**
  3. * @package Grav.Common.Backup
  4. *
  5. * @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Common\Backup;
  9. use Grav\Common\Grav;
  10. use Grav\Common\Inflector;
  11. class ZipBackup
  12. {
  13. protected static $ignorePaths = [
  14. 'backup',
  15. 'cache',
  16. 'images',
  17. 'logs',
  18. 'tmp'
  19. ];
  20. protected static $ignoreFolders = [
  21. '.git',
  22. '.svn',
  23. '.hg',
  24. '.idea',
  25. 'node_modules'
  26. ];
  27. /**
  28. * Backup
  29. *
  30. * @param string|null $destination
  31. * @param callable|null $messager
  32. *
  33. * @return null|string
  34. */
  35. public static function backup($destination = null, callable $messager = null)
  36. {
  37. if (!$destination) {
  38. $destination = Grav::instance()['locator']->findResource('backup://', true);
  39. if (!$destination) {
  40. throw new \RuntimeException('The backup folder is missing.');
  41. }
  42. }
  43. $name = substr(strip_tags(Grav::instance()['config']->get('site.title', basename(GRAV_ROOT))), 0, 20);
  44. $inflector = new Inflector();
  45. if (is_dir($destination)) {
  46. $date = date('YmdHis', time());
  47. $filename = trim($inflector->hyphenize($name), '-') . '-' . $date . '.zip';
  48. $destination = rtrim($destination, DS) . DS . $filename;
  49. }
  50. $messager && $messager([
  51. 'type' => 'message',
  52. 'level' => 'info',
  53. 'message' => 'Creating new Backup "' . $destination . '"'
  54. ]);
  55. $messager && $messager([
  56. 'type' => 'message',
  57. 'level' => 'info',
  58. 'message' => ''
  59. ]);
  60. $zip = new \ZipArchive();
  61. $zip->open($destination, \ZipArchive::CREATE);
  62. $max_execution_time = ini_set('max_execution_time', 600);
  63. static::folderToZip(GRAV_ROOT, $zip, strlen(rtrim(GRAV_ROOT, DS) . DS), $messager);
  64. $messager && $messager([
  65. 'type' => 'progress',
  66. 'percentage' => false,
  67. 'complete' => true
  68. ]);
  69. $messager && $messager([
  70. 'type' => 'message',
  71. 'level' => 'info',
  72. 'message' => ''
  73. ]);
  74. $messager && $messager([
  75. 'type' => 'message',
  76. 'level' => 'info',
  77. 'message' => 'Saving and compressing archive...'
  78. ]);
  79. $zip->close();
  80. if ($max_execution_time !== false) {
  81. ini_set('max_execution_time', $max_execution_time);
  82. }
  83. return $destination;
  84. }
  85. /**
  86. * @param $folder
  87. * @param $zipFile
  88. * @param $exclusiveLength
  89. * @param $messager
  90. */
  91. private static function folderToZip($folder, \ZipArchive $zipFile, $exclusiveLength, callable $messager = null)
  92. {
  93. $handle = opendir($folder);
  94. while (false !== $f = readdir($handle)) {
  95. if ($f !== '.' && $f !== '..') {
  96. $filePath = "$folder/$f";
  97. // Remove prefix from file path before add to zip.
  98. $localPath = substr($filePath, $exclusiveLength);
  99. if (in_array($f, static::$ignoreFolders)) {
  100. continue;
  101. }
  102. if (in_array($localPath, static::$ignorePaths)) {
  103. $zipFile->addEmptyDir($f);
  104. continue;
  105. }
  106. if (is_file($filePath)) {
  107. $zipFile->addFile($filePath, $localPath);
  108. $messager && $messager([
  109. 'type' => 'progress',
  110. 'percentage' => false,
  111. 'complete' => false
  112. ]);
  113. } elseif (is_dir($filePath)) {
  114. // Add sub-directory.
  115. $zipFile->addEmptyDir($localPath);
  116. static::folderToZip($filePath, $zipFile, $exclusiveLength, $messager);
  117. }
  118. }
  119. }
  120. closedir($handle);
  121. }
  122. }