PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 3.8.0
WP STAGING – WordPress Backup, Restore, Migration & Clone v3.8.0
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 2 years ago Database 2 years ago AbstractServiceProvider.php 1 year ago Archiver.php 1 year ago BackupAssets.php 2 years ago BackupMetadataEditor.php 3 years ago BackupSigner.php 2 years ago BackupsFinder.php 1 year ago Extractor.php 2 years ago FileBackupService.php 1 year ago FileBackupServiceProvider.php 1 year ago ServiceInterface.php 1 year ago ZlibCompressor.php 1 year ago
FileBackupService.php
228 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\Backup\Dto\StepsDto;
8 use WPStaging\Backup\Exceptions\BackupSkipItemException;
9 use WPStaging\Backup\Exceptions\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. No-op, as this is handled elsewhere.
108 }
109 }
110
111 if ($this->bigFileBeingProcessed instanceof ArchiverDto) {
112 $relativePathForLogging = str_replace($this->filesystem->normalizePath(ABSPATH, true), '', $this->filesystem->normalizePath($this->bigFileBeingProcessed->getFilePath(), true));
113 $percentProcessed = ceil(($this->bigFileBeingProcessed->getWrittenBytesTotal() / $this->bigFileBeingProcessed->getFileSize()) * 100);
114 $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()));
115 } else {
116 $this->logger->info(sprintf('Added %d/%d %s files to backup (%s)', $this->stepsDto->getCurrent(), $this->stepsDto->getTotal(), $this->fileIdentifier, $this->getBackupSpeed()));
117 }
118
119 $this->updateMultipartInfo();
120 }
121
122 /**
123 * @throws DiskNotWritableException
124 * @throws FinishedQueueException
125 * @return void
126 */
127 public function backup()
128 {
129 $archiverDto = $this->archiver->getDto();
130 $archiverDto->setWrittenBytesTotal($this->jobDataDto->getFileBeingBackupWrittenBytes());
131
132 if ($archiverDto->getWrittenBytesTotal() !== 0) {
133 $archiverDto->setIndexPositionCreated(true);
134 }
135
136 $path = $this->taskQueue->dequeue();
137 $path = $this->replacePlaceholdersWithEOLs($path);
138
139 if (is_null($path)) {
140 debug_log("Backup error: no task to dequeue");
141 throw new FinishedQueueException();
142 }
143
144 if (empty($path)) {
145 return;
146 }
147
148 $indexPath = '';
149 if (strpos($path, FilesystemScannerTask::PATH_SEPARATOR) !== false) {
150 list($path, $indexPath) = explode(FilesystemScannerTask::PATH_SEPARATOR, $path);
151 }
152
153 if ($this->shouldPrependAbsPath()) {
154 $path = trailingslashit(ABSPATH) . $path;
155 }
156
157 try {
158 $this->maybeIncrementPartNo($path);
159 $isFileWrittenCompletely = $this->archiver->appendFileToBackup($path, $indexPath);
160 } catch (BackupSkipItemException $e) {
161 $isFileWrittenCompletely = null;
162 } catch (\RuntimeException $e) {
163 debug_log("Backup error: cannot append file to backup: $path");
164 $isFileWrittenCompletely = null;
165 }
166
167 // Done processing this file
168 if ($isFileWrittenCompletely === true) {
169 $this->jobDataDto->setFileBeingBackupWrittenBytes(0);
170 $this->stepsDto->incrementCurrentStep();
171
172 return;
173 } elseif ($isFileWrittenCompletely === null) {
174 // Invalid file
175 $this->logger->warning("Invalid file. Could not add file to backup: $path");
176 $this->jobDataDto->setFileBeingBackupWrittenBytes(0);
177 $this->stepsDto->incrementCurrentStep();
178 debug_log("Backup error: cannot append file to backup: $path");
179
180 return;
181 } elseif ($isFileWrittenCompletely === false) {
182 // Processing a file that could not be finished in this request
183 $this->jobDataDto->setFileBeingBackupWrittenBytes($archiverDto->getWrittenBytesTotal());
184 $this->taskQueue->retry(false);
185
186 if ($archiverDto->getWrittenBytesTotal() < $archiverDto->getFileSize() && $archiverDto->getFileSize() > 10 * MB_IN_BYTES) {
187 $this->bigFileBeingProcessed = $archiverDto;
188 }
189 }
190 }
191
192 protected function getBackupSpeed(): string
193 {
194 $elapsed = microtime(true) - $this->start;
195 // Fixes a "division by zero fatal error" when $elapsed was 0. issue #2571
196 $elapsed = empty($elapsed) ? 1 : $elapsed;
197
198 $bytesPerSecond = min(10 * GB_IN_BYTES, absint($this->archiver->getBytesWrittenInThisRequest() / $elapsed));
199
200 if ($bytesPerSecond === 10 * GB_IN_BYTES) {
201 return '10GB/s+';
202 }
203
204 return size_format($bytesPerSecond) . '/s';
205 }
206
207 protected function shouldPrependAbsPath(): bool
208 {
209 return $this->isWpContentOutsideAbspath === false;
210 }
211
212 /**
213 * @return void
214 */
215 protected function updateMultipartInfo()
216 {
217 // Used in Pro
218 }
219
220 /**
221 * @return void
222 */
223 protected function maybeIncrementPartNo(string $path)
224 {
225 // Used in Pro
226 }
227 }
228