-
Notifications
You must be signed in to change notification settings - Fork 1
/
Task.php
350 lines (304 loc) · 8.04 KB
/
Task.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
<?php
namespace NewfoldLabs\WP\Module\Tasks\Models\Data;
/**
* Tracks and stores a task in the task table.
* A task will have task_id, task_name, args, num_retries, interval and enabled as fields
*/
final class Task {
/**
* The task id
*
* @var int
*/
public $task_id;
/**
* The task name
*
* @var string
*/
public $task_name;
/**
* The arguments to be used when executing the task
*
* @var array
*/
public $args;
/**
* The number of times we need to retry the task
*
* @var int
*/
public $num_retries;
/**
* The intervals in seconds after which we need to retry the task
*
* @var int
*/
public $task_interval;
/**
* The priority for this task, defaults to 1
*
* @var int
*/
public $task_priority;
/**
* If the task is enabled or not
*
* @var boolean
*/
public $enabled;
/**
* The task executor function
*
* @var string
*/
public $task_execute;
/**
* The task status, should be one of queued, processing, and finished.
* Defaults to queued.
*
* @var string
*/
public $task_status;
/**
* Updated timestamp
*
* @var datetime
*/
public $updated;
/**
* Setter function to assign a task name
*
* @param string $task_name the task name to use
*/
public function set_task_name( $task_name ) {
$this->task_name = $task_name;
return $this;
}
/**
* Setter function to set the args
*
* @param array $args the args as an associative array
*/
public function set_args( $args ) {
$this->args = $args;
return $this;
}
/**
* Setter function to set num_retries
*
* @param int $num_retries the number of retries to set for the task
*/
public function set_num_retries( $num_retries ) {
$this->num_retries = $num_retries;
return $this;
}
/**
* Setter function to set the task interval
*
* @param int $interval the interval in seconds to use for task, only required for periodic tasks
*/
public function set_task_interval( $interval ) {
$this->task_interval = $interval;
return $this;
}
/**
* Setter function to set the task priority
*
* @param int $priority the priority to set for task
*/
public function set_task_priority( $priority ) {
$this->task_priority = $priority;
return $this;
}
/**
* Setter function to set task enabled status
*
* @param boolean $enabled the task's enabled status
*/
public function set_enabled( $enabled ) {
$this->enabled = $enabled;
return $this;
}
/**
* Setter function to set the task executable
*
* @param string $task_execute the function to run when executing this task
*/
public function set_task_execute( $task_execute ) {
$this->task_execute = $task_execute;
return $this;
}
/**
* Set task status
*
* @param string $status one of processing, queued and finished
*/
public function set_task_status( $status ) {
$this->task_status = $status;
return $this;
}
/**
* The constructor to create the table if not already present
*
* @param int $id The task id, null by default, only pass it to initialize a task object with id
*/
public function __construct( $id = null ) {
global $wpdb;
// Initialize the task attributes
$this->task_id = $id;
if ( ! function_exists( 'wp_json_encode' ) ) {
require_once ABSPATH . 'wp-includes/functions.php';
}
$table_name = MODULE_TASKS_TASK_TABLE_NAME;
if ( $id ) {
// Get and populate the task with the required stuff
$task = $wpdb->get_row(
// phpcs:ignore
$wpdb->prepare( "select * from `{$wpdb->prefix}{$table_name}` where `task_id` = %d", $id )
);
// Populate the task details from what we got
$this->task_id = $task->task_id;
$this->task_name = $task->task_name;
$this->task_execute = $task->task_execute;
$this->args = json_decode( $task->args, true );
$this->num_retries = $task->num_retries;
$this->task_interval = $task->task_interval;
$this->task_priority = $task->task_priority;
$this->enabled = 1 === $task->enabled;
$this->task_status = $task->task_status;
$this->updated = $task->updated;
}
}
/**
* A function to queue a task, this will ensure the db entry for the task
*/
public function queue_task() {
global $wpdb;
$table_name = MODULE_TASKS_TASK_TABLE_NAME;
if ( ! $this->task_id ) {
// Create an entry
$wpdb->insert(
$wpdb->prefix . $table_name,
array(
'task_name' => $this->task_name,
'task_execute' => $this->task_execute,
'args' => wp_json_encode( $this->args ),
'num_retries' => $this->num_retries,
'task_interval' => $this->task_interval,
'task_priority' => $this->task_priority || 10,
'enabled' => $this->enabled ? 1 : 0,
'task_status' => 'queued',
)
);
$this->task_id = $wpdb->insert_id;
$this->task_status = 'queued';
// If the task is periodic, just add the filter for WordPress cron to pick it up
if ( $this->task_interval ) {
$this->add_periodic_task_as_cron();
}
}
}
/**
* Add a test execute function. Only usable for one-off tasks
*
* @throws \Exception Exception when we cannot find the task executable.
*/
public function execute() {
if ( ! is_callable( $this->task_execute ) ) {
throw new \Exception( 'Unable to load task' );
}
call_user_func( $this->task_execute, $this->args );
}
/**
* A function that deletes any given task
*/
public function delete() {
global $wpdb;
$wpdb->delete( $wpdb->prefix . MODULE_TASKS_TASK_TABLE_NAME, array( 'task_id' => $this->task_id ) );
}
/**
* Function to add an arbitrary number of seconds as cron interval.
* To be used in add_filter when setting up the cron
*/
public function add_interval_schedule() {
$interval = $this->task_interval;
$key = "{$interval}_seconds";
$message = "Once every {$interval} seconds";
$current_schedules = get_option( 'wp_module_tasks_schedules', array() );
if ( ! $current_schedules ) {
add_option( 'wp_module_tasks_schedules', array() );
}
// Adds the schedule for the given intervals in seconds
if ( ! array_key_exists( $key, $current_schedules ) || $interval !== $current_schedules[ $key ]['interval'] ) {
$current_schedules[ $key ] = array(
'interval' => $interval,
// phpcs:ignore
'display' => $message,
);
}
update_option( 'wp_module_tasks_schedules', $current_schedules );
}
/**
* Function to update the task's status
*
* @param string $status One of queued, processing, and finished.
*/
public function update_task_status( $status ) {
global $wpdb;
$table_name = MODULE_TASKS_TASK_TABLE_NAME;
if ( ! in_array( $status, array( 'processing', 'queued', 'finished' ), true ) ) {
return;
}
$wpdb->update(
$wpdb->prefix . $table_name,
array( 'task_status' => $status ),
array( 'task_id' => $this->task_id )
);
}
/**
* Function to get all the tasks with a given task name
*
* @param string $task_name The task name
*/
public static function get_tasks_with_name( $task_name ) {
global $wpdb;
$table_name = MODULE_TASKS_TASK_TABLE_NAME;
$required_tasks = $wpdb->get_results(
$wpdb->prepare(
// phpcs:ignore
"select * from `{$wpdb->prefix}{$table_name}` where task_name = %s", $task_name
)
);
return $required_tasks;
}
/**
* Function to get the tasks which are in processing state for quite some time
*/
public static function get_timed_out_tasks() {
global $wpdb;
$table_name = MODULE_TASKS_TASK_TABLE_NAME;
// Get the tasks with processing status and updated more than 2 hours
$stuck_tasks = $wpdb->get_results(
//phpcs:ignore
"SELECT * FROM `{$wpdb->prefix}{$table_name}` WHERE task_status = \'processing\' AND updated < DATE_SUB(NOW(), INTERVAL 10 MINUTE)"
);
return $stuck_tasks;
}
/**
* Add the task to WordPress cron
*/
public function add_periodic_task_as_cron() {
// Add the cron schedule
$this->add_interval_schedule();
// Register the cron task
if ( ! wp_next_scheduled( 'task_execution_hook', array( $this->task_id ) ) ) {
wp_schedule_event(
time(),
"{$this->task_interval}_seconds",
'task_execution_hook',
array( $this->task_id )
);
}
}
}