PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 3.3.1
WP STAGING – WordPress Backup, Restore, Migration & Clone v3.3.1
4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Backup / Task / FileBackupTask.php
wp-staging / Backup / Task Last commit date
RestoreFileHandlers 2 years ago Tasks 2 years ago AbstractTask.php 2 years ago BackupTask.php 3 years ago FileBackupTask.php 2 years ago FileRestoreTask.php 2 years ago RestoreTask.php 2 years ago
FileBackupTask.php
211 lines
1 <?php
2
3 // TODO PHP7.x; declare(strict_type=1);
4 // TODO PHP7.x; type hints & return types
5 // TODO PHP7.1; constant visibility
6
7 namespace WPStaging\Backup\Task;
8
9 use WPStaging\Backup\Dto\Service\CompressorDto;
10 use WPStaging\Backup\Dto\StepsDto;
11 use WPStaging\Backup\Exceptions\DiskNotWritableException;
12 use WPStaging\Backup\Service\Compressor;
13 use WPStaging\Backup\Service\Multipart\MultipartSplitInterface;
14 use WPStaging\Backup\Task\BackupTask;
15 use WPStaging\Backup\Task\Tasks\JobBackup\FilesystemScannerTask;
16 use WPStaging\Core\WPStaging;
17 use WPStaging\Framework\Filesystem\Filesystem;
18 use WPStaging\Framework\Queue\FinishedQueueException;
19 use WPStaging\Framework\Queue\SeekableQueueInterface;
20 use WPStaging\Framework\SiteInfo;
21 use WPStaging\Framework\Utils\Cache\Cache;
22 use WPStaging\Vendor\Psr\Log\LoggerInterface;
23
24 use function WPStaging\functions\debug_log;
25
26 abstract class FileBackupTask extends BackupTask
27 {
28 /** @var Compressor */
29 private $compressor;
30
31 /** @var int|CompressorDto If a file couldn't be processed in a single request, this will be populated */
32 private $bigFileBeingProcessed;
33
34 /** @var Filesystem */
35 protected $filesystem;
36
37 /** @var float */
38 protected $start;
39
40 /** @var MultipartSplitInterface */
41 protected $multipartSplit;
42
43 /** @var bool */
44 protected $isWpContentOutsideAbspath = false;
45
46 public function __construct(Compressor $compressor, LoggerInterface $logger, Cache $cache, StepsDto $stepsDto, SeekableQueueInterface $taskQueue, Filesystem $filesystem, MultipartSplitInterface $multipartSplit)
47 {
48 parent::__construct($logger, $cache, $stepsDto, $taskQueue);
49 $this->compressor = $compressor;
50 $this->filesystem = $filesystem;
51 $this->multipartSplit = $multipartSplit;
52 }
53
54 public static function getTaskName()
55 {
56 return 'backup_file_task';
57 }
58
59 public static function getTaskTitle()
60 {
61 return 'Adding Files to Backup';
62 }
63
64 public function execute()
65 {
66 $this->prepareFileBackupTask();
67 $this->setupCompressor();
68 $this->start = microtime(true);
69
70 /** @var SiteInfo */
71 $siteInfo = WPStaging::make(SiteInfo::class);
72 $this->isWpContentOutsideAbspath = $siteInfo->isWpContentOutsideAbspath();
73
74 while (!$this->isThreshold() && !$this->stepsDto->isFinished()) {
75 try {
76 $this->backup();
77 } catch (FinishedQueueException $exception) {
78 $this->stepsDto->finish();
79
80 if ($this->jobDataDto->getIsMultipartBackup() && $this->stepsDto->getTotal() > 0) {
81 $this->multipartSplit->setBackupPartInfo($this->jobDataDto, $this->compressor);
82 }
83
84 $this->logger->info(sprintf('Added %d/%d %s files to backup (%s)', $this->stepsDto->getCurrent(), $this->stepsDto->getTotal(), $this->getFileIdentifier(), $this->getBackupSpeed()));
85
86 return $this->generateResponse(false);
87 } catch (DiskNotWritableException $exception) {
88 // Probably disk full. No-op, as this is handled elsewhere.
89 }
90 }
91
92 if ($this->bigFileBeingProcessed instanceof CompressorDto) {
93 $relativePathForLogging = str_replace($this->filesystem->normalizePath(ABSPATH, true), '', $this->filesystem->normalizePath($this->bigFileBeingProcessed->getFilePath(), true));
94 $percentProcessed = ceil(($this->bigFileBeingProcessed->getWrittenBytesTotal() / $this->bigFileBeingProcessed->getFileSize()) * 100);
95 $this->logger->info(sprintf('Adding big %s file: %s - %s/%s (%s%%) (%s)', $this->getFileIdentifier(), $relativePathForLogging, size_format($this->bigFileBeingProcessed->getWrittenBytesTotal(), 2), size_format($this->bigFileBeingProcessed->getFileSize(), 2), $percentProcessed, $this->getBackupSpeed()));
96 } else {
97 $this->logger->info(sprintf('Added %d/%d %s files to backup (%s)', $this->stepsDto->getCurrent(), $this->stepsDto->getTotal(), $this->getFileIdentifier(), $this->getBackupSpeed()));
98 }
99
100 if ($this->stepsDto->isFinished() && $this->jobDataDto->getIsMultipartBackup() && $this->stepsDto->getTotal() > 0) {
101 $this->multipartSplit->setBackupPartInfo($this->jobDataDto, $this->compressor);
102 }
103
104 return $this->generateResponse(false);
105 }
106
107 /** @return string */
108 abstract protected function getFileIdentifier();
109
110 protected function setupCompressor()
111 {
112 if (!$this->jobDataDto->getIsMultipartBackup() || !WPStaging::isPro()) {
113 $this->compressor->setCategory('', $create = true);
114 return;
115 }
116
117 $this->multipartSplit->setupCompressor($this->jobDataDto, $this->compressor, $this->getFileIdentifier(), $this->stepsDto->getTotal() > 0);
118 }
119
120 protected function getBackupSpeed()
121 {
122 $elapsed = microtime(true) - $this->start;
123 // Fixes a "division by zero fatal error" when $elapsed was 0. issue #2571
124 $elapsed = empty($elapsed) ? 1 : $elapsed;
125
126 $bytesPerSecond = min(10 * GB_IN_BYTES, absint($this->compressor->getBytesWrittenInThisRequest() / $elapsed));
127
128 if ($bytesPerSecond === 10 * GB_IN_BYTES) {
129 return '10GB/s+';
130 }
131
132 return size_format($bytesPerSecond) . '/s';
133 }
134
135 /**
136 * @throws DiskNotWritableException
137 * @throws FinishedQueueException
138 */
139 public function backup()
140 {
141 $compressorDto = $this->compressor->getDto();
142 $compressorDto->setWrittenBytesTotal($this->jobDataDto->getFileBeingBackupWrittenBytes());
143
144 if ($compressorDto->getWrittenBytesTotal() !== 0) {
145 $compressorDto->setIndexPositionCreated(true);
146 }
147
148 $path = $this->taskQueue->dequeue();
149
150 if (is_null($path)) {
151 debug_log("Backup error: no task to dequeue");
152 throw new FinishedQueueException();
153 }
154
155 if (empty($path)) {
156 //$this->logger->warning("Path is empty. Cannot add file to backup.");
157 return;
158 }
159
160 $indexPath = '';
161 if (strpos($path, FilesystemScannerTask::PATH_SEPARATOR) !== false) {
162 list($path, $indexPath) = explode(FilesystemScannerTask::PATH_SEPARATOR, $path);
163 }
164
165 if ($this->isWpContentOutsideAbspath === false) {
166 $path = trailingslashit(ABSPATH) . $path;
167 }
168
169 try {
170 $this->multipartSplit->maybeIncrementBackupFileIndex($this->jobDataDto, $this->compressor, $this->getFileIdentifier(), $path);
171 $isFileWrittenCompletely = $this->compressor->appendFileToBackup($path, $indexPath);
172 } catch (\RuntimeException $e) {
173 debug_log("Backup error: cannot append file to backup: $path");
174 $isFileWrittenCompletely = null;
175 }
176
177 // Done processing this file
178 if ($isFileWrittenCompletely === true) {
179 $this->jobDataDto->setFileBeingBackupWrittenBytes(0);
180 $this->stepsDto->incrementCurrentStep();
181
182 return;
183 } elseif ($isFileWrittenCompletely === null) {
184 // Invalid file
185 $this->logger->warning("Invalid file. Could not add file to backup: $path");
186 $this->jobDataDto->setFileBeingBackupWrittenBytes(0);
187 $this->stepsDto->incrementCurrentStep();
188 debug_log("Backup error: cannot append file to backup: $path");
189
190 return;
191 } elseif ($isFileWrittenCompletely === false) {
192 // Processing a file that could not be finished in this request
193 $this->jobDataDto->setFileBeingBackupWrittenBytes($compressorDto->getWrittenBytesTotal());
194 $this->taskQueue->retry(false);
195
196 if ($compressorDto->getWrittenBytesTotal() < $compressorDto->getFileSize() && $compressorDto->getFileSize() > 10 * MB_IN_BYTES) {
197 $this->bigFileBeingProcessed = $compressorDto;
198 }
199 }
200 }
201
202 private function prepareFileBackupTask()
203 {
204 if ($this->stepsDto->getTotal() > 0) {
205 return;
206 }
207
208 $this->stepsDto->setTotal($this->jobDataDto->getDiscoveredFilesByCategory($this->getFileIdentifier()));
209 }
210 }
211