PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 3.1.3
WP STAGING – WordPress Backup, Restore, Migration & Clone v3.1.3
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 3 years ago RestoreTask.php 2 years ago
FileBackupTask.php
203 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\Core\WPStaging;
16 use WPStaging\Framework\Filesystem\Filesystem;
17 use WPStaging\Framework\Queue\FinishedQueueException;
18 use WPStaging\Framework\Queue\SeekableQueueInterface;
19 use WPStaging\Framework\SiteInfo;
20 use WPStaging\Framework\Utils\Cache\Cache;
21 use WPStaging\Vendor\Psr\Log\LoggerInterface;
22
23 use function WPStaging\functions\debug_log;
24
25 abstract class FileBackupTask extends BackupTask
26 {
27 /** @var Compressor */
28 private $compressor;
29
30 /** @var int|CompressorDto If a file couldn't be processed in a single request, this will be populated */
31 private $bigFileBeingProcessed;
32
33 /** @var Filesystem */
34 protected $filesystem;
35
36 /** @var float */
37 protected $start;
38
39 /** @var MultipartSplitInterface */
40 protected $multipartSplit;
41
42 /** @var bool */
43 protected $isWpContentOutsideAbspath = false;
44
45 public function __construct(Compressor $compressor, LoggerInterface $logger, Cache $cache, StepsDto $stepsDto, SeekableQueueInterface $taskQueue, Filesystem $filesystem, MultipartSplitInterface $multipartSplit)
46 {
47 parent::__construct($logger, $cache, $stepsDto, $taskQueue);
48 $this->compressor = $compressor;
49 $this->filesystem = $filesystem;
50 $this->multipartSplit = $multipartSplit;
51 }
52
53 public static function getTaskName()
54 {
55 return 'backup_file_task';
56 }
57
58 public static function getTaskTitle()
59 {
60 return 'Adding Files to Backup';
61 }
62
63 public function execute()
64 {
65 $this->prepareFileBackupTask();
66 $this->setupCompressor();
67 $this->start = microtime(true);
68
69 $this->isWpContentOutsideAbspath = WPStaging::make(SiteInfo::class)->isWpContentOutsideAbspath();
70
71 while (!$this->isThreshold() && !$this->stepsDto->isFinished()) {
72 try {
73 $this->backup();
74 } catch (FinishedQueueException $exception) {
75 $this->stepsDto->finish();
76
77 if ($this->jobDataDto->getIsMultipartBackup() && $this->stepsDto->getTotal() > 0) {
78 $this->multipartSplit->setBackupPartInfo($this->jobDataDto, $this->compressor);
79 }
80
81 $this->logger->info(sprintf('Added %d/%d %s files to backup (%s)', $this->stepsDto->getCurrent(), $this->stepsDto->getTotal(), $this->getFileIdentifier(), $this->getBackupSpeed()));
82
83 return $this->generateResponse(false);
84 } catch (DiskNotWritableException $exception) {
85 // Probably disk full. No-op, as this is handled elsewhere.
86 }
87 }
88
89 if ($this->bigFileBeingProcessed instanceof CompressorDto) {
90 $relativePathForLogging = str_replace($this->filesystem->normalizePath(ABSPATH, true), '', $this->filesystem->normalizePath($this->bigFileBeingProcessed->getFilePath(), true));
91 $percentProcessed = ceil(($this->bigFileBeingProcessed->getWrittenBytesTotal() / $this->bigFileBeingProcessed->getFileSize()) * 100);
92 $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()));
93 } else {
94 $this->logger->info(sprintf('Added %d/%d %s files to backup (%s)', $this->stepsDto->getCurrent(), $this->stepsDto->getTotal(), $this->getFileIdentifier(), $this->getBackupSpeed()));
95 }
96
97 if ($this->stepsDto->isFinished() && $this->jobDataDto->getIsMultipartBackup() && $this->stepsDto->getTotal() > 0) {
98 $this->multipartSplit->setBackupPartInfo($this->jobDataDto, $this->compressor);
99 }
100
101 return $this->generateResponse(false);
102 }
103
104 /** @return string */
105 abstract protected function getFileIdentifier();
106
107 protected function setupCompressor()
108 {
109 if (!$this->jobDataDto->getIsMultipartBackup() || !WPStaging::isPro()) {
110 $this->compressor->setCategory('', $create = true);
111 return;
112 }
113
114 $this->multipartSplit->setupCompressor($this->jobDataDto, $this->compressor, $this->getFileIdentifier(), $this->stepsDto->getTotal() > 0);
115 }
116
117 protected function getBackupSpeed()
118 {
119 $elapsed = microtime(true) - $this->start;
120 // Fixes a "division by zero fatal error" when $elapsed was 0. issue #2571
121 $elapsed = empty($elapsed) ? 1 : $elapsed;
122
123 $bytesPerSecond = min(10 * GB_IN_BYTES, absint($this->compressor->getBytesWrittenInThisRequest() / $elapsed));
124
125 if ($bytesPerSecond === 10 * GB_IN_BYTES) {
126 return '10GB/s+';
127 }
128
129 return size_format($bytesPerSecond) . '/s';
130 }
131
132 /**
133 * @throws DiskNotWritableException
134 * @throws FinishedQueueException
135 */
136 public function backup()
137 {
138 $compressorDto = $this->compressor->getDto();
139 $compressorDto->setWrittenBytesTotal($this->jobDataDto->getFileBeingBackupWrittenBytes());
140
141 if ($compressorDto->getWrittenBytesTotal() !== 0) {
142 $compressorDto->setIndexPositionCreated(true);
143 }
144
145 $path = $this->taskQueue->dequeue();
146
147 if (is_null($path)) {
148 debug_log("Backup error: no task to dequeue");
149 throw new FinishedQueueException();
150 }
151
152 if (empty($path)) {
153 //$this->logger->warning("Path is empty. Cannot add file to backup.");
154 return;
155 }
156
157 if ($this->isWpContentOutsideAbspath === false) {
158 $path = trailingslashit(ABSPATH) . $path;
159 }
160
161 try {
162 $this->multipartSplit->maybeIncrementBackupFileIndex($this->jobDataDto, $this->compressor, $this->getFileIdentifier(), $path);
163 $isFileWrittenCompletely = $this->compressor->appendFileToBackup($path);
164 } catch (\RuntimeException $e) {
165 debug_log("Backup error: cannot append file to backup: $path");
166 $isFileWrittenCompletely = null;
167 }
168
169 // Done processing this file
170 if ($isFileWrittenCompletely === true) {
171 $this->jobDataDto->setFileBeingBackupWrittenBytes(0);
172 $this->stepsDto->incrementCurrentStep();
173
174 return;
175 } elseif ($isFileWrittenCompletely === null) {
176 // Invalid file
177 $this->logger->warning("Invalid file. Could not add file to backup: $path");
178 $this->jobDataDto->setFileBeingBackupWrittenBytes(0);
179 $this->stepsDto->incrementCurrentStep();
180 debug_log("Backup error: cannot append file to backup: $path");
181
182 return;
183 } elseif ($isFileWrittenCompletely === false) {
184 // Processing a file that could not be finished in this request
185 $this->jobDataDto->setFileBeingBackupWrittenBytes($compressorDto->getWrittenBytesTotal());
186 $this->taskQueue->retry(false);
187
188 if ($compressorDto->getWrittenBytesTotal() < $compressorDto->getFileSize() && $compressorDto->getFileSize() > 10 * MB_IN_BYTES) {
189 $this->bigFileBeingProcessed = $compressorDto;
190 }
191 }
192 }
193
194 private function prepareFileBackupTask()
195 {
196 if ($this->stepsDto->getTotal() > 0) {
197 return;
198 }
199
200 $this->stepsDto->setTotal($this->jobDataDto->getDiscoveredFilesByCategory($this->getFileIdentifier()));
201 }
202 }
203