PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 3.8.6
WP STAGING – WordPress Backup, Restore, Migration & Clone v3.8.6
4.9.3 4.9.2 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 / Service / FileBackupService.php
wp-staging / Backup / Service Last commit date
Compression 1 year ago Database 1 year ago AbstractBackupsFinder.php 1 year ago AbstractExtractor.php 1 year ago AbstractServiceProvider.php 2 years ago Archiver.php 1 year ago BackupAssets.php 2 years ago BackupContent.php 1 year ago BackupMetadataEditor.php 1 year ago BackupMetadataReader.php 1 year ago BackupSigner.php 1 year ago BackupsFinder.php 1 year ago Extractor.php 1 year ago FileBackupService.php 1 year ago FileBackupServiceProvider.php 2 years ago ServiceInterface.php 2 years ago ZlibCompressor.php 2 years ago
FileBackupService.php
233 lines
1 <?php
2
3 namespace WPStaging\Backup\Service;
4
5 use WPStaging\Backup\Dto\Job\JobBackupDataDto;
6 use WPStaging\Backup\Dto\Service\ArchiverDto;
7 use WPStaging\Framework\Job\Dto\StepsDto;
8 use WPStaging\Backup\Exceptions\BackupSkipItemException;
9 use WPStaging\Framework\Job\Exception\DiskNotWritableException;
10 use WPStaging\Backup\Task\Tasks\JobBackup\FilesystemScannerTask;
11 use WPStaging\Framework\Filesystem\Filesystem;
12 use WPStaging\Framework\Queue\FinishedQueueException;
13 use WPStaging\Framework\Queue\SeekableQueueInterface;
14 use WPStaging\Framework\SiteInfo;
15 use WPStaging\Framework\Traits\EndOfLinePlaceholderTrait;
16 use WPStaging\Framework\Traits\ResourceTrait;
17 use WPStaging\Vendor\Psr\Log\LoggerInterface;
18
19 use function WPStaging\functions\debug_log;
20
21 class FileBackupService implements ServiceInterface
22 {
23 use ResourceTrait;
24 use EndOfLinePlaceholderTrait;
25
26 /** @var Archiver */
27 protected $archiver;
28
29 /** @var Filesystem */
30 protected $filesystem;
31
32 /** @var SeekableQueueInterface */
33 protected $taskQueue;
34
35 /** @var LoggerInterface */
36 protected $logger;
37
38 /** @var JobBackupDataDto */
39 protected $jobDataDto;
40
41 /** @var StepsDto */
42 protected $stepsDto;
43
44 /** @var int|ArchiverDto If a file couldn't be processed in a single request, this will be populated */
45 protected $bigFileBeingProcessed;
46
47 /** @var bool */
48 protected $isWpContentOutsideAbspath = false;
49
50 /** @var float */
51 protected $start;
52
53 /** @var string */
54 protected $fileIdentifier;
55
56 public function __construct(Archiver $archiver, Filesystem $filesystem, SiteInfo $siteInfo)
57 {
58 $this->archiver = $archiver;
59 $this->filesystem = $filesystem;
60
61 $this->isWpContentOutsideAbspath = $siteInfo->isWpContentOutsideAbspath();
62 }
63
64 /**
65 * @param SeekableQueueInterface $taskQueue
66 * @param LoggerInterface $logger
67 * @param JobBackupDataDto $jobDataDto
68 * @param StepsDto $stepsDto
69 * @return void
70 */
71 public function inject(SeekableQueueInterface $taskQueue, LoggerInterface $logger, JobBackupDataDto $jobDataDto, StepsDto $stepsDto)
72 {
73 $this->taskQueue = $taskQueue;
74 $this->logger = $logger;
75 $this->jobDataDto = $jobDataDto;
76 $this->stepsDto = $stepsDto;
77 }
78
79 /**
80 * @param string $fileIdentifier
81 * @param bool $isOtherWpRootFilesTask (Used in Pro)
82 * @return void
83 */
84 public function setupArchiver(string $fileIdentifier, bool $isOtherWpRootFilesTask = false)
85 {
86 $this->fileIdentifier = $fileIdentifier;
87 $this->archiver->createArchiveFile(Archiver::CREATE_BINARY_HEADER);
88 }
89
90 /**
91 * @return void
92 */
93 public function execute()
94 {
95 $this->start = microtime(true);
96
97 while (!$this->isThreshold() && !$this->stepsDto->isFinished()) {
98 try {
99 $this->backup();
100 } catch (FinishedQueueException $exception) {
101 $this->stepsDto->finish();
102 $this->logger->info(sprintf('Added %d/%d %s files to backup (%s)', $this->stepsDto->getCurrent(), $this->stepsDto->getTotal(), $this->fileIdentifier, $this->getBackupSpeed()));
103 $this->updateMultipartInfo();
104
105 return;
106 } catch (DiskNotWritableException $exception) {
107 // Probably disk full. Should be handled in Job\AbstractJob::prepareAndExecute(). Let's stop the code here if it did not happen!
108 throw new \Exception('Disk is probably full. Error message: ' . $exception->getMessage());
109 } catch (\Throwable $th) {
110 throw new \Exception('Fail to create backup. Error message: ' . $th->getMessage());
111 }
112 }
113
114 if ($this->bigFileBeingProcessed instanceof ArchiverDto) {
115 $relativePathForLogging = str_replace($this->filesystem->normalizePath(ABSPATH, true), '', $this->filesystem->normalizePath($this->bigFileBeingProcessed->getFilePath(), true));
116 $percentProcessed = ceil(($this->bigFileBeingProcessed->getWrittenBytesTotal() / $this->bigFileBeingProcessed->getFileSize()) * 100);
117 $this->logger->info(sprintf('Adding big %s file: %s - %s/%s (%s%%) (%s)', $this->fileIdentifier, $relativePathForLogging, size_format($this->bigFileBeingProcessed->getWrittenBytesTotal(), 2), size_format($this->bigFileBeingProcessed->getFileSize(), 2), $percentProcessed, $this->getBackupSpeed()));
118 } else {
119 $this->logger->info(sprintf('Added %d/%d %s files to backup (%s)', $this->stepsDto->getCurrent(), $this->stepsDto->getTotal(), $this->fileIdentifier, $this->getBackupSpeed()));
120 }
121
122 $this->updateMultipartInfo();
123 }
124
125 /**
126 * @throws DiskNotWritableException
127 * @throws FinishedQueueException
128 * @return void
129 */
130 public function backup()
131 {
132 $archiverDto = $this->archiver->getDto();
133 $archiverDto->setWrittenBytesTotal($this->jobDataDto->getFileBeingBackupWrittenBytes());
134
135 if ($archiverDto->getWrittenBytesTotal() !== 0) {
136 $archiverDto->setIndexPositionCreated(true);
137 }
138
139 $path = $this->taskQueue->dequeue();
140 $path = $this->replacePlaceholdersWithEOLs($path);
141
142 if (is_null($path)) {
143 debug_log("Backup error: no task to dequeue");
144 throw new FinishedQueueException();
145 }
146
147 if (empty($path)) {
148 return;
149 }
150
151 $indexPath = '';
152 if (strpos($path, FilesystemScannerTask::PATH_SEPARATOR) !== false) {
153 list($path, $indexPath) = explode(FilesystemScannerTask::PATH_SEPARATOR, $path);
154 }
155
156 if ($this->shouldPrependAbsPath()) {
157 $path = trailingslashit(ABSPATH) . $path;
158 }
159
160 try {
161 $this->maybeIncrementPartNo($path);
162 $isFileWrittenCompletely = $this->archiver->appendFileToBackup($path, $indexPath);
163 } catch (BackupSkipItemException $e) {
164 $isFileWrittenCompletely = null;
165 } catch (\RuntimeException $e) {
166 debug_log("Backup error: cannot append file to backup: $path");
167 $isFileWrittenCompletely = null;
168 } catch (\Throwable $th) {
169 throw $th;
170 }
171
172 // Done processing this file
173 if ($isFileWrittenCompletely === true) {
174 $this->jobDataDto->setFileBeingBackupWrittenBytes(0);
175 $this->stepsDto->incrementCurrentStep();
176
177 return;
178 } elseif ($isFileWrittenCompletely === null) {
179 // Invalid file
180 $this->logger->warning("Invalid file. Could not add file to backup: $path");
181 $this->jobDataDto->setFileBeingBackupWrittenBytes(0);
182 $this->stepsDto->incrementCurrentStep();
183 debug_log("Backup error: cannot append file to backup: $path");
184
185 return;
186 } elseif ($isFileWrittenCompletely === false) {
187 // Processing a file that could not be finished in this request
188 $this->jobDataDto->setFileBeingBackupWrittenBytes($archiverDto->getWrittenBytesTotal());
189 $this->taskQueue->retry(false);
190
191 if ($archiverDto->getWrittenBytesTotal() < $archiverDto->getFileSize() && $archiverDto->getFileSize() > 10 * MB_IN_BYTES) {
192 $this->bigFileBeingProcessed = $archiverDto;
193 }
194 }
195 }
196
197 protected function getBackupSpeed(): string
198 {
199 $elapsed = microtime(true) - $this->start;
200 // Fixes a "division by zero fatal error" when $elapsed was 0. issue #2571
201 $elapsed = empty($elapsed) ? 1 : $elapsed;
202
203 $bytesPerSecond = min(10 * GB_IN_BYTES, absint($this->archiver->getBytesWrittenInThisRequest() / $elapsed));
204
205 if ($bytesPerSecond === 10 * GB_IN_BYTES) {
206 return '10GB/s+';
207 }
208
209 return size_format($bytesPerSecond) . '/s';
210 }
211
212 protected function shouldPrependAbsPath(): bool
213 {
214 return $this->isWpContentOutsideAbspath === false;
215 }
216
217 /**
218 * @return void
219 */
220 protected function updateMultipartInfo()
221 {
222 // Used in Pro
223 }
224
225 /**
226 * @return void
227 */
228 protected function maybeIncrementPartNo(string $path)
229 {
230 // Used in Pro
231 }
232 }
233