PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / 4.9.1
WP STAGING – WordPress Backup, Restore, Migration & Clone v4.9.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 / Framework / Job / JobTransientCache.php
wp-staging / Framework / Job Last commit date
Ajax 1 month ago BackgroundProcessing 1 year ago Dto 1 week ago Exception 3 months ago Interfaces 1 week ago Jobs 5 months ago Task 1 week ago Traits 8 months ago AbstractJob.php 1 month ago JobProvider.php 1 year ago JobServiceProvider.php 4 months ago JobTransientCache.php 1 month ago ProcessLock.php 1 year ago
JobTransientCache.php
300 lines
1 <?php
2
3 namespace WPStaging\Framework\Job;
4
5 /**
6 * It is used to cache the current running job data in a transient.
7 * So it can be used for BackgroundLogger to push the events to the SSE stream.
8 */
9 class JobTransientCache
10 {
11 /**
12 * This is the time in seconds that the job transient will be kept.
13 * This is used to show the current job status in the UI.
14 * Transient will be deleted automatically after this time if not deleted manually in case of inactivity.
15 * @var int
16 */
17 const JOB_TRANSIENT_EXPIRY = 60 * 6; // 6 minutes
18
19 /**
20 * This is the time in seconds that the job transient will be kept after the job is completed.
21 * Instead of deleting the transient immediately, we reduce it expiry to 15 seconds, to every open SSE stream
22 * can get the latest status of the job.
23 * @var int
24 */
25 const JOB_TRANSIENT_EXPIRY_ON_COMPLETE = 15;
26
27 /**
28 * This is the transient key that will be used to store the current job data.
29 * @var string
30 */
31 const TRANSIENT_CURRENT_JOB = 'wpstg_current_job';
32
33 /**
34 * @var string
35 */
36 const STATUS_RUNNING = 'running';
37
38 /**
39 * @var string
40 */
41 const STATUS_SUCCESS = 'success';
42
43 /**
44 * @var string
45 */
46 const STATUS_FAILED = 'failed';
47
48 /**
49 * @var string
50 */
51 const STATUS_CANCELLED = 'cancelled';
52
53 /**
54 * @var string
55 */
56 const STATUS_STALED = 'staled';
57
58 /**
59 * @var string
60 */
61 const JOB_TYPE_BACKUP = 'Backup';
62
63 /**
64 * @var string
65 */
66 const JOB_TYPE_RESTORE = 'Restore';
67
68 /**
69 * @var string
70 */
71 const JOB_TYPE_EXTRACT = 'Extract';
72
73 /**
74 * @var string
75 */
76 const JOB_TYPE_CANCEL = 'Cancel';
77
78 /**
79 * @var string
80 */
81 const JOB_TYPE_PLUGINS_UPDATER = 'Plugins_Updater';
82
83 /**
84 * @var string
85 */
86 const JOB_TYPE_STAGING_CREATE = 'Staging_Create';
87
88 /**
89 * @var string
90 */
91 const JOB_TYPE_STAGING_UPDATE = 'Staging_Update';
92
93 /**
94 * @var string
95 */
96 const JOB_TYPE_STAGING_RESET = 'Staging_Reset';
97
98 /**
99 * @var string
100 */
101 const JOB_TYPE_STAGING_PUSH = 'Staging_Push';
102
103 /**
104 * @var string
105 */
106 const JOB_TYPE_STAGING_DELETE = 'Staging_Delete';
107
108 /**
109 * @var string
110 */
111 const JOB_TYPE_PULL_PREPARE = 'Pull_Prepare';
112
113 /**
114 * @var string
115 */
116 const JOB_TYPE_PULL_RESTORE = 'Pull_Restore';
117
118 /**
119 * @var string
120 */
121 const JOB_TYPE_REMOTE_UPLOAD = 'Remote_Upload';
122
123 /**
124 * @var string[]
125 */
126 const CANCELABLE_JOBS = [
127 self::JOB_TYPE_BACKUP,
128 self::JOB_TYPE_RESTORE,
129 self::JOB_TYPE_EXTRACT,
130 self::JOB_TYPE_PULL_PREPARE,
131 self::JOB_TYPE_PULL_RESTORE,
132 self::JOB_TYPE_STAGING_CREATE,
133 self::JOB_TYPE_STAGING_UPDATE,
134 self::JOB_TYPE_STAGING_RESET,
135 self::JOB_TYPE_REMOTE_UPLOAD,
136 ];
137
138 /**
139 * @param string $jobId
140 * @param string $jobTitle
141 * @param string $jobType
142 * @param string $queueId
143 * @return void
144 */
145 public function startJob(string $jobId, string $jobTitle, string $jobType = 'job', string $queueId = '')
146 {
147 $jobData = [
148 'jobId' => $jobId,
149 'title' => $jobTitle,
150 'type' => $jobType,
151 'status' => self::STATUS_RUNNING,
152 'startedAt' => time(),
153 'updatedAt' => time(),
154 'queueId' => $queueId,
155 'message' => '',
156 ];
157
158 delete_transient(self::TRANSIENT_CURRENT_JOB);
159 set_transient(self::TRANSIENT_CURRENT_JOB, $jobData, self::JOB_TRANSIENT_EXPIRY);
160 }
161
162 /**
163 * Mark the current job as pre-initialized so the SSE endpoint can detect
164 * if the background queue never started processing.
165 * When the background queue picks up the job and calls startJob() again,
166 * the preInitAt field is automatically removed (startJob creates fresh data).
167 */
168 public function markAsPreInitialized()
169 {
170 $jobData = $this->getJob();
171 if ($jobData === null) {
172 return;
173 }
174
175 $jobData['preInitAt'] = time();
176 delete_transient(self::TRANSIENT_CURRENT_JOB);
177 set_transient(self::TRANSIENT_CURRENT_JOB, $jobData, self::JOB_TRANSIENT_EXPIRY);
178 }
179
180 /**
181 * @param string $title
182 * @return void
183 */
184 public function updateTitle(string $title)
185 {
186 $jobData = $this->getJob();
187 $jobData['title'] = $title;
188 $jobData['updatedAt'] = time();
189
190 set_transient(self::TRANSIENT_CURRENT_JOB, $jobData, self::JOB_TRANSIENT_EXPIRY);
191 }
192
193 /**
194 * @return void
195 */
196 public function completeJob()
197 {
198 $this->stopJob(self::STATUS_SUCCESS);
199 }
200
201 /**
202 * @return void
203 */
204 public function cancelJob(string $jobTitle)
205 {
206 $this->stopJob(self::STATUS_CANCELLED, $jobTitle);
207 }
208
209 /**
210 * @param string $title
211 * @param string $message
212 * @param string $severity 'error' (default, red-X modal) or 'notice'
213 * (info-style modal for expected no-ops such as
214 * a Remote Sync selection that resolves to zero
215 * files on the source).
216 * @return void
217 */
218 public function failJob(string $title = '', string $message = '', string $severity = '')
219 {
220 $this->stopJob(self::STATUS_FAILED, $title, $message, $severity);
221 }
222
223 /**
224 * @return array|null
225 */
226 public function getJob()
227 {
228 $jobData = get_transient(self::TRANSIENT_CURRENT_JOB);
229 if (empty($jobData['jobId'])) {
230 return null;
231 }
232
233 return $jobData;
234 }
235
236 public function getJobId(): string
237 {
238 $jobData = $this->getJob();
239 if (empty($jobData['jobId'])) {
240 return '';
241 }
242
243 return $jobData['jobId'];
244 }
245
246 public function getJobStatus(): string
247 {
248 $jobData = $this->getJob();
249 if (empty($jobData['status'])) {
250 return '';
251 }
252
253 return $jobData['status'];
254 }
255
256 /**
257 * @return void
258 */
259 public function update()
260 {
261 $jobData = $this->getJob();
262 $jobData['updatedAt'] = time();
263
264 delete_transient(self::TRANSIENT_CURRENT_JOB);
265 set_transient(self::TRANSIENT_CURRENT_JOB, $jobData, self::JOB_TRANSIENT_EXPIRY);
266 }
267
268 /**
269 * @param string $status
270 * @param string $title
271 * @param string $message
272 * @return void
273 */
274 private function stopJob(string $status, string $title = '', string $message = '', string $severity = '')
275 {
276 $jobData = $this->getJob();
277 $jobData['status'] = $status;
278 $jobData['updatedAt'] = time();
279 if (!empty($title)) {
280 $jobData['title'] = $title;
281 }
282
283 if (!empty($message)) {
284 $jobData['message'] = $message;
285 }
286
287 // Severity is only used for failed jobs. Empty string means "don't
288 // overwrite" — lets a caller that ran earlier (e.g. the Remote Sync
289 // hook) preserve its classification when the generic PrepareJob
290 // finalisation later calls failJob() without a severity.
291 if ($severity !== '') {
292 $jobData['severity'] = $severity;
293 }
294
295 // This will make sure to update the expiry as well if the status was already the same!
296 delete_transient(self::TRANSIENT_CURRENT_JOB);
297 set_transient(self::TRANSIENT_CURRENT_JOB, $jobData, self::JOB_TRANSIENT_EXPIRY_ON_COMPLETE);
298 }
299 }
300