PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.8.4
ووسلام – همگام سازی ووکامرس و باسلام v1.8.4
1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.2 1.9.1 1.9.0 1.8.8 1.8.5 1.8.6 1.8.7 1.8.4 1.7.6 All 50 releases
sync-basalam / AsyncBackgroundProcess.php

AsyncBackgroundProcess.php in ووسلام – همگام سازی ووکامرس و باسلام 1.8.4, at AsyncBackgroundProcess.php

392 lines 8.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace SyncBasalam;
4
5 defined('ABSPATH') || exit;
6
7 abstract class AsyncBackgroundProcess
8 {
9 protected $action = 'async_process';
10
11 protected $identifier;
12
13 protected $data = array();
14
15 protected $timeLimit = 30;
16
17 protected $batchSize = 20;
18
19 protected $cronHook;
20
21 protected $cronInterval = 'every_5_minutes';
22
23 public function __construct()
24 {
25 $this->identifier = $this->getIdentifier();
26 $this->cronHook = $this->identifier . '_cron';
27
28 add_filter('cron_schedules', array($this, 'addCronInterval'));
29
30 add_action('wp_ajax_' . $this->identifier, array($this, 'handleAsyncRequest'));
31 add_action('wp_ajax_nopriv_' . $this->identifier, array($this, 'handleAsyncRequest'));
32 add_action($this->cronHook, array($this, 'handleCronHealthcheck'));
33
34 $this->init();
35 }
36
37 protected function init() {}
38
39 protected function getIdentifier()
40 {
41 return $this->action . '_' . substr(md5(get_class($this)), 0, 8);
42 }
43
44 public function addCronInterval($schedules)
45 {
46 $schedules['every_5_minutes'] = array(
47 'interval' => 300,
48 'display' => __('Every 5 Minutes')
49 );
50
51 $schedules['every_minute'] = array(
52 'interval' => 60,
53 'display' => __('Every Minute')
54 );
55
56 return $schedules;
57 }
58
59 public function push($item)
60 {
61 $this->data[] = $item;
62 return $this;
63 }
64
65 public function save()
66 {
67 $key = $this->getBatchKey();
68
69 if (!empty($this->data)) {
70 update_option($key, $this->data, false);
71 }
72
73 $this->data = array();
74
75 return $this;
76 }
77
78 public function dispatch()
79 {
80
81 if (!wp_next_scheduled($this->cronHook)) {
82 wp_schedule_event(time(), $this->cronInterval, $this->cronHook);
83 }
84
85 return $this->triggerAsyncRequest();
86 }
87
88 protected function triggerAsyncRequest()
89 {
90 $url = add_query_arg(
91 array(
92 'action' => $this->identifier,
93 'nonce' => wp_create_nonce($this->identifier)
94 ),
95 admin_url('admin-ajax.php')
96 );
97
98 $args = array(
99 'timeout' => 0.01,
100 'blocking' => false,
101 'body' => array(
102 'action' => $this->identifier,
103 'nonce' => wp_create_nonce($this->identifier)
104 ),
105 'cookies' => $_COOKIE,
106 'sslverify' => apply_filters('https_local_ssl_verify', false),
107 'headers' => array(
108 'X-WP-Async-Request' => $this->identifier
109 )
110 );
111
112 $result = wp_remote_post(esc_url_raw($url), $args);
113
114 if (is_wp_error($result)) return $this->triggerAlternativeAsync();
115
116 return $result;
117 }
118
119 protected function triggerAlternativeAsync()
120 {
121 wp_schedule_single_event(time(), $this->identifier . '_immediate');
122 add_action($this->identifier . '_immediate', array($this, 'handleAsyncRequest'));
123
124 spawn_cron();
125
126 return true;
127 }
128
129 public function handleAsyncRequest()
130 {
131 session_write_close();
132 if (!$this->isProcessing()) {
133 $this->handle();
134 }
135 }
136
137 public function handleCronHealthcheck()
138 {
139 if ($this->isQueueEmpty()) {
140
141 $this->clearScheduledEvent();
142 return;
143 }
144
145 $this->triggerAsyncRequest();
146 }
147
148 protected function handle()
149 {
150
151 $this->lockProcess();
152
153 $batch = $this->getBatch();
154
155 if (empty($batch)) {
156 $this->unlockProcess();
157 return;
158 }
159
160 $startTime = time();
161
162 foreach ($batch as $key => $item) {
163 if ($this->timeExceeded($startTime)) {
164 break;
165 }
166
167 if ($this->memoryExceeded()) {
168 break;
169 }
170
171 $item = $this->task($item);
172
173 if (false === $item) {
174 unset($batch[$key]);
175 } else {
176 $batch[$key] = $item;
177 }
178 }
179
180 if (!empty($batch)) {
181
182 $this->updateBatch($batch);
183 } else {
184
185 $this->deleteBatch();
186 }
187
188 $this->unlockProcess();
189
190 if (!$this->isQueueEmpty()) {
191
192 $this->dispatch();
193 } else {
194
195 $this->complete();
196 $this->clearScheduledEvent();
197 }
198 }
199
200 abstract protected function task($item);
201
202 protected function complete() {}
203
204 protected function getBatch()
205 {
206 global $wpdb;
207
208 $table = $wpdb->options;
209 $keyPattern = $this->identifier . '_batch_%';
210
211 $query = $wpdb->prepare("
212 SELECT option_name, option_value
213 FROM {$table}
214 WHERE option_name LIKE %s
215 ORDER BY option_name ASC
216 LIMIT 1
217 ", $keyPattern);
218
219 $batch = $wpdb->get_row($query);
220
221 if (empty($batch)) return array();
222
223 $batchData = maybe_unserialize($batch->option_value);
224
225 if (!is_array($batchData)) return array();
226
227 return array_slice($batchData, 0, $this->batchSize, true);
228 }
229
230 protected function updateBatch($batch)
231 {
232 $batches = $this->getBatches();
233
234 if (!empty($batches)) {
235 $firstBatch = array_shift($batches);
236 update_option($firstBatch->option_name, $batch, false);
237 }
238 }
239
240 protected function deleteBatch()
241 {
242 $batches = $this->getBatches();
243
244 if (!empty($batches)) {
245 $firstBatch = array_shift($batches);
246 delete_option($firstBatch->option_name);
247 }
248 }
249
250 protected function getBatches()
251 {
252 global $wpdb;
253
254 $table = $wpdb->options;
255 $keyPattern = $this->identifier . '_batch_%';
256
257 return $wpdb->get_results(
258 $wpdb->prepare("
259 SELECT option_name
260 FROM {$table}
261 WHERE option_name LIKE %s
262 ORDER BY option_name ASC
263 ", $keyPattern)
264 );
265 }
266
267 protected function getBatchKey()
268 {
269 $key = $this->identifier . '_batch_' . md5(microtime() . mt_rand());
270
271 $count = 1;
272 while (get_option($key) !== false) {
273 $key = $this->identifier . '_batch_' . md5(microtime() . mt_rand() . $count);
274 $count++;
275 }
276
277 return $key;
278 }
279
280 protected function isQueueEmpty()
281 {
282 $batches = $this->getBatches();
283 return empty($batches);
284 }
285
286 protected function lockProcess()
287 {
288 $lockKey = $this->identifier . '_lock';
289 $lockDuration = 60;
290
291 $lock = get_transient($lockKey);
292
293 if ($lock) return false;
294
295 set_transient($lockKey, microtime(true), $lockDuration);
296
297 return true;
298 }
299
300 protected function unlockProcess()
301 {
302 delete_transient($this->identifier . '_lock');
303 }
304
305 protected function isProcessing()
306 {
307 return (bool) get_transient($this->identifier . '_lock');
308 }
309
310 protected function timeExceeded($startTime)
311 {
312 $timeLimit = ini_get('max_execution_time');
313
314 if (empty($timeLimit) || $timeLimit > $this->timeLimit) {
315 $timeLimit = $this->timeLimit;
316 }
317
318 $timeLimit = $timeLimit - 5;
319
320 return (time() - $startTime) > $timeLimit;
321 }
322
323 protected function memoryExceeded()
324 {
325 $memoryLimit = ini_get('memory_limit');
326
327 if ($memoryLimit == '-1') return false;
328
329 $memoryLimit = $this->convertToBytes($memoryLimit);
330 $currentMemory = memory_get_usage(true);
331
332 $buffer = 10 * 1024 * 1024;
333
334 return ($currentMemory + $buffer) > $memoryLimit;
335 }
336
337 protected function convertToBytes($value)
338 {
339 $value = strtolower(trim($value));
340 $bytes = (int) $value;
341
342 if (strpos($value, 'g') !== false) {
343 $bytes *= 1024 * 1024 * 1024;
344 } elseif (strpos($value, 'm') !== false) {
345 $bytes *= 1024 * 1024;
346 } elseif (strpos($value, 'k') !== false) {
347 $bytes *= 1024;
348 }
349
350 return $bytes;
351 }
352
353 protected function clearScheduledEvent()
354 {
355 $timestamp = wp_next_scheduled($this->cronHook);
356
357 if ($timestamp) {
358 wp_unschedule_event($timestamp, $this->cronHook);
359 }
360 }
361
362 public function cancel()
363 {
364 $batches = $this->getBatches();
365
366 foreach ($batches as $batch) {
367 delete_option($batch->option_name);
368 }
369
370 $this->clearScheduledEvent();
371
372 $this->unlockProcess();
373 }
374
375 public function isActive()
376 {
377 if (get_transient($this->identifier . '_lock')) return true;
378
379 $batches = $this->getBatches();
380
381 $hasScheduledCron = wp_next_scheduled($this->cronHook) !== false;
382
383 return !empty($batches) || $hasScheduledCron;
384 }
385
386 public function countBatches()
387 {
388 $batches = $this->getBatches();
389 return count($batches);
390 }
391 }
392