Filesystem.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Framework\Backup;
  7. use Magento\Framework\App\ObjectManager;
  8. use Magento\Framework\Archive\Gz;
  9. use Magento\Framework\Backup\Archive\Tar;
  10. use Magento\Framework\Backup\Exception\NotEnoughFreeSpace;
  11. use Magento\Framework\Backup\Exception\NotEnoughPermissions;
  12. use Magento\Framework\Backup\Filesystem\Helper;
  13. use Magento\Framework\Backup\Filesystem\Rollback\Fs;
  14. use Magento\Framework\Backup\Filesystem\Rollback\Ftp;
  15. use Magento\Framework\Exception\LocalizedException;
  16. use Magento\Framework\Phrase;
  17. /**
  18. * Class to work with filesystem backups
  19. *
  20. * @author Magento Core Team <core@magentocommerce.com>
  21. */
  22. class Filesystem extends AbstractBackup
  23. {
  24. /**
  25. * Paths that ignored when creating or rolling back snapshot
  26. *
  27. * @var array
  28. */
  29. protected $_ignorePaths = [];
  30. /**
  31. * Whether use ftp account for rollback procedure
  32. *
  33. * @var bool
  34. */
  35. protected $_useFtp = false;
  36. /**
  37. * Ftp host
  38. *
  39. * @var string
  40. */
  41. protected $_ftpHost;
  42. /**
  43. * Ftp username
  44. *
  45. * @var string
  46. */
  47. protected $_ftpUser;
  48. /**
  49. * Password to ftp account
  50. *
  51. * @var string
  52. */
  53. protected $_ftpPass;
  54. /**
  55. * Ftp path to Magento installation
  56. *
  57. * @var string
  58. */
  59. protected $_ftpPath;
  60. /**
  61. * @var Ftp
  62. */
  63. protected $rollBackFtp;
  64. /**
  65. * @var Fs
  66. */
  67. protected $rollBackFs;
  68. /**
  69. * Implementation Rollback functionality for Filesystem
  70. *
  71. * @throws LocalizedException
  72. * @return bool
  73. */
  74. public function rollback()
  75. {
  76. $this->_lastOperationSucceed = false;
  77. set_time_limit(0);
  78. ignore_user_abort(true);
  79. $rollbackWorker = $this->_useFtp ? $this->getRollBackFtp() : $this->getRollBackFs();
  80. $rollbackWorker->run();
  81. $this->_lastOperationSucceed = true;
  82. return $this->_lastOperationSucceed;
  83. }
  84. /**
  85. * Implementation Create Backup functionality for Filesystem
  86. *
  87. * @throws LocalizedException
  88. * @return boolean
  89. */
  90. public function create()
  91. {
  92. set_time_limit(0);
  93. ignore_user_abort(true);
  94. $this->_lastOperationSucceed = false;
  95. $this->_checkBackupsDir();
  96. $fsHelper = new Helper();
  97. $filesInfo = $fsHelper->getInfo(
  98. $this->getRootDir(),
  99. Helper::INFO_READABLE |
  100. Helper::INFO_SIZE,
  101. $this->getIgnorePaths()
  102. );
  103. if (!$filesInfo['readable']) {
  104. throw new NotEnoughPermissions(
  105. new Phrase('Not enough permissions to read files for backup')
  106. );
  107. }
  108. $this->validateAvailableDiscSpace($this->getBackupsDir(), $filesInfo['size']);
  109. $tarTmpPath = $this->_getTarTmpPath();
  110. $tarPacker = new Tar();
  111. $tarPacker->setSkipFiles($this->getIgnorePaths())->pack($this->getRootDir(), $tarTmpPath, true);
  112. if (!is_file($tarTmpPath) || filesize($tarTmpPath) == 0) {
  113. throw new LocalizedException(
  114. new Phrase('Failed to create backup')
  115. );
  116. }
  117. $backupPath = $this->getBackupPath();
  118. $gzPacker = new Gz();
  119. $gzPacker->pack($tarTmpPath, $backupPath);
  120. if (!is_file($backupPath) || filesize($backupPath) == 0) {
  121. throw new LocalizedException(
  122. new Phrase('Failed to create backup')
  123. );
  124. }
  125. @unlink($tarTmpPath);
  126. $this->_lastOperationSucceed = true;
  127. return $this->_lastOperationSucceed;
  128. }
  129. /**
  130. * Validate if disk space is available for creating backup
  131. *
  132. * @param string $backupDir
  133. * @param int $size
  134. * @return void
  135. * @throws LocalizedException
  136. */
  137. public function validateAvailableDiscSpace($backupDir, $size)
  138. {
  139. $freeSpace = disk_free_space($backupDir);
  140. $requiredSpace = 2 * $size;
  141. if ($requiredSpace > $freeSpace) {
  142. throw new NotEnoughFreeSpace(
  143. new Phrase(
  144. 'Warning: necessary space for backup is ' . (ceil($requiredSpace) / 1024)
  145. . 'MB, but your free disc space is ' . (ceil($freeSpace) / 1024) . 'MB.'
  146. )
  147. );
  148. }
  149. }
  150. /**
  151. * Force class to use ftp for rollback procedure
  152. *
  153. * @param string $host
  154. * @param string $username
  155. * @param string $password
  156. * @param string $path
  157. * @return $this
  158. */
  159. public function setUseFtp($host, $username, $password, $path)
  160. {
  161. $this->_useFtp = true;
  162. $this->_ftpHost = $host;
  163. $this->_ftpUser = $username;
  164. $this->_ftpPass = $password;
  165. $this->_ftpPath = $path;
  166. return $this;
  167. }
  168. /**
  169. * Get backup type
  170. *
  171. * @return string
  172. *
  173. * @see BackupInterface::getType()
  174. */
  175. public function getType()
  176. {
  177. return 'filesystem';
  178. }
  179. /**
  180. * Add path that should be ignoring when creating or rolling back backup
  181. *
  182. * @param string|array $paths
  183. * @return $this
  184. */
  185. public function addIgnorePaths($paths)
  186. {
  187. if (is_string($paths)) {
  188. if (!in_array($paths, $this->_ignorePaths)) {
  189. $this->_ignorePaths[] = $paths;
  190. }
  191. } elseif (is_array($paths)) {
  192. foreach ($paths as $path) {
  193. $this->addIgnorePaths($path);
  194. }
  195. }
  196. return $this;
  197. }
  198. /**
  199. * Get paths that should be ignored while creating or rolling back backup procedure
  200. *
  201. * @return array
  202. */
  203. public function getIgnorePaths()
  204. {
  205. return $this->_ignorePaths;
  206. }
  207. /**
  208. * Set directory where backups saved and add it to ignore paths
  209. *
  210. * @param string $backupsDir
  211. * @return $this
  212. *
  213. * @see AbstractBackup::setBackupsDir()
  214. */
  215. public function setBackupsDir($backupsDir)
  216. {
  217. parent::setBackupsDir($backupsDir);
  218. $this->addIgnorePaths($backupsDir);
  219. return $this;
  220. }
  221. /**
  222. * Getter for $_ftpPath variable
  223. *
  224. * @return string
  225. */
  226. public function getFtpPath()
  227. {
  228. return $this->_ftpPath;
  229. }
  230. /**
  231. * Get ftp connection string
  232. *
  233. * @return string
  234. */
  235. public function getFtpConnectString()
  236. {
  237. return 'ftp://' . $this->_ftpUser . ':' . $this->_ftpPass . '@' . $this->_ftpHost . $this->_ftpPath;
  238. }
  239. /**
  240. * Check backups directory existence and whether it's writeable
  241. *
  242. * @return void
  243. * @throws LocalizedException
  244. */
  245. protected function _checkBackupsDir()
  246. {
  247. $backupsDir = $this->getBackupsDir();
  248. if (!is_dir($backupsDir)) {
  249. $backupsDirParentDirectory = basename($backupsDir);
  250. if (!is_writeable($backupsDirParentDirectory)) {
  251. throw new NotEnoughPermissions(
  252. new Phrase('Cant create backups directory')
  253. );
  254. }
  255. mkdir($backupsDir);
  256. chmod($backupsDir);
  257. }
  258. if (!is_writable($backupsDir)) {
  259. throw new NotEnoughPermissions(
  260. new Phrase('Backups directory is not writeable')
  261. );
  262. }
  263. }
  264. /**
  265. * Generate tmp name for tarball
  266. *
  267. * @return string
  268. */
  269. protected function _getTarTmpPath()
  270. {
  271. $tmpName = '~tmp-' . microtime(true) . '.tar';
  272. return $this->getBackupsDir() . '/' . $tmpName;
  273. }
  274. /**
  275. * @return Ftp
  276. * @deprecated 101.0.0
  277. */
  278. protected function getRollBackFtp()
  279. {
  280. if (!$this->rollBackFtp) {
  281. $this->rollBackFtp = ObjectManager::getInstance()->create(
  282. Ftp::class,
  283. ['snapshotObject' => $this]
  284. );
  285. }
  286. return $this->rollBackFtp;
  287. }
  288. /**
  289. * @return Fs
  290. * @deprecated 101.0.0
  291. */
  292. protected function getRollBackFs()
  293. {
  294. if (!$this->rollBackFs) {
  295. $this->rollBackFs = ObjectManager::getInstance()->create(
  296. Fs::class,
  297. ['snapshotObject' => $this]
  298. );
  299. }
  300. return $this->rollBackFs;
  301. }
  302. }