1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Functions to support schedules tasks |
5
|
|
|
* |
6
|
|
|
* @name ElkArte Forum |
7
|
|
|
* @copyright ElkArte Forum contributors |
8
|
|
|
* @license BSD http://opensource.org/licenses/BSD-3-Clause |
9
|
|
|
* |
10
|
|
|
* @version 1.1 |
11
|
|
|
* |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* Calculate the next time the passed tasks should be triggered. |
16
|
|
|
* |
17
|
|
|
* @package ScheduledTasks |
18
|
|
|
* @param string[]|string $tasks = array() the tasks |
19
|
|
|
* @param boolean $forceUpdate |
20
|
|
|
*/ |
21
|
|
|
function calculateNextTrigger($tasks = array(), $forceUpdate = false) |
22
|
|
|
{ |
23
|
|
|
global $modSettings; |
24
|
|
|
|
25
|
|
|
$db = database(); |
26
|
|
|
|
27
|
|
|
$task_query = ''; |
28
|
|
|
|
29
|
|
|
if (!is_array($tasks)) |
30
|
|
|
$tasks = array($tasks); |
31
|
|
|
|
32
|
|
|
// Actually have something passed? |
33
|
|
|
if (!empty($tasks)) |
34
|
|
|
{ |
35
|
|
|
if (!isset($tasks[0]) || is_numeric($tasks[0])) |
36
|
|
|
$task_query = ' AND id_task IN ({array_int:tasks})'; |
37
|
|
|
else |
38
|
|
|
$task_query = ' AND task IN ({array_string:tasks})'; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
$nextTaskTime = empty($tasks) ? time() + 86400 : $modSettings['next_task_time']; |
42
|
|
|
|
43
|
|
|
// Get the critical info for the tasks. |
44
|
|
|
$request = $db->query('', ' |
45
|
|
|
SELECT id_task, next_time, time_offset, time_regularity, time_unit, task |
46
|
|
|
FROM {db_prefix}scheduled_tasks |
47
|
|
|
WHERE disabled = {int:no_disabled} |
48
|
|
|
' . $task_query, |
49
|
|
|
array( |
50
|
|
|
'no_disabled' => 0, |
51
|
|
|
'tasks' => $tasks, |
52
|
|
|
) |
53
|
|
|
); |
54
|
|
|
$tasks = array(); |
55
|
|
|
$scheduleTaskImmediate = !empty($modSettings['scheduleTaskImmediate']) ? Util::unserialize($modSettings['scheduleTaskImmediate']) : array(); |
56
|
|
|
while ($row = $db->fetch_assoc($request)) |
57
|
|
|
{ |
58
|
|
|
// scheduleTaskImmediate is a way to speed up scheduled tasks and fire them as fast as possible |
59
|
|
|
if (!empty($scheduleTaskImmediate) && isset($scheduleTaskImmediate[$row['task']])) |
60
|
|
|
$next_time = next_time(1, '', rand(0, 60), true); |
61
|
|
|
else |
62
|
|
|
$next_time = next_time($row['time_regularity'], $row['time_unit'], $row['time_offset']); |
63
|
|
|
|
64
|
|
|
// Only bother moving the task if it's out of place or we're forcing it! |
65
|
|
|
if ($forceUpdate || $next_time < $row['next_time'] || $row['next_time'] < time()) |
66
|
|
|
$tasks[$row['id_task']] = $next_time; |
67
|
|
|
else |
68
|
|
|
$next_time = $row['next_time']; |
69
|
|
|
|
70
|
|
|
// If this is sooner than the current next task, make this the next task. |
71
|
|
|
if ($next_time < $nextTaskTime) |
72
|
|
|
$nextTaskTime = $next_time; |
73
|
|
|
} |
74
|
|
|
$db->free_result($request); |
75
|
|
|
|
76
|
|
|
// Now make the changes! |
77
|
|
|
foreach ($tasks as $id => $time) |
78
|
|
|
$db->query('', ' |
79
|
|
|
UPDATE {db_prefix}scheduled_tasks |
80
|
|
|
SET next_time = {int:next_time} |
81
|
|
|
WHERE id_task = {int:id_task}', |
82
|
|
|
array( |
83
|
|
|
'next_time' => $time, |
84
|
|
|
'id_task' => $id, |
85
|
|
|
) |
86
|
|
|
); |
87
|
|
|
|
88
|
|
|
// If the next task is now different update. |
89
|
|
|
if ($modSettings['next_task_time'] != $nextTaskTime) |
90
|
|
|
updateSettings(array('next_task_time' => $nextTaskTime)); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
/** |
94
|
|
|
* Returns a time stamp of the next instance of these time parameters. |
95
|
|
|
* |
96
|
|
|
* @package ScheduledTasks |
97
|
|
|
* @param int $regularity |
98
|
|
|
* @param string $unit |
99
|
|
|
* @param int $offset |
100
|
|
|
* @param boolean $immediate |
101
|
|
|
* @return int |
102
|
|
|
*/ |
103
|
|
|
function next_time($regularity, $unit, $offset, $immediate = false) |
104
|
|
|
{ |
105
|
|
|
// Just in case! |
106
|
4 |
|
if ($regularity == 0) |
107
|
4 |
|
$regularity = 2; |
108
|
|
|
|
109
|
4 |
|
$curMin = date('i', time()); |
110
|
|
|
|
111
|
|
|
// If we have scheduleTaskImmediate running, then it's 10 seconds |
112
|
4 |
|
if (empty($unit) && $immediate) |
113
|
4 |
|
$next_time = time() + 10; |
114
|
|
|
// If the unit is minutes only check regularity in minutes. |
115
|
4 |
|
elseif ($unit == 'm') |
116
|
|
|
{ |
117
|
|
|
$off = date('i', $offset); |
118
|
|
|
|
119
|
|
|
// If it's now just pretend it ain't, |
120
|
|
|
if ($off == $curMin) |
121
|
|
|
$next_time = time() + $regularity; |
122
|
|
|
else |
123
|
|
|
{ |
124
|
|
|
// Make sure that the offset is always in the past. |
125
|
|
|
$off = $off > $curMin ? $off - 60 : $off; |
126
|
|
|
|
127
|
|
|
while ($off <= $curMin) |
128
|
|
|
$off += $regularity; |
129
|
|
|
|
130
|
|
|
// Now we know when the time should be! |
131
|
|
|
$next_time = time() + 60 * ($off - $curMin); |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
// Otherwise, work out what the offset would be with todays date. |
135
|
|
|
else |
136
|
|
|
{ |
137
|
4 |
|
$next_time = mktime(date('H', $offset), date('i', $offset), 0, date('m'), date('d'), date('Y')); |
138
|
|
|
|
139
|
|
|
// Make the time offset in the past! |
140
|
4 |
|
if ($next_time > time()) |
141
|
4 |
|
$next_time -= 86400; |
142
|
|
|
|
143
|
|
|
// Default we'll jump in hours. |
144
|
4 |
|
$applyOffset = 3600; |
145
|
|
|
|
146
|
|
|
// 24 hours = 1 day. |
147
|
4 |
|
if ($unit == 'd') |
148
|
4 |
|
$applyOffset = 86400; |
149
|
|
|
|
150
|
|
|
// Otherwise a week. |
151
|
4 |
|
if ($unit == 'w') |
152
|
4 |
|
$applyOffset = 604800; |
153
|
|
|
|
154
|
4 |
|
$applyOffset *= $regularity; |
155
|
|
|
|
156
|
|
|
// Just add on the offset. |
157
|
4 |
|
while ($next_time <= time()) |
158
|
4 |
|
$next_time += $applyOffset; |
159
|
|
|
} |
160
|
|
|
|
161
|
4 |
|
return $next_time; |
162
|
|
|
} |
163
|
|
|
|
164
|
|
|
/** |
165
|
|
|
* Loads a basic tasks list. |
166
|
|
|
* |
167
|
|
|
* @package ScheduledTasks |
168
|
|
|
* @param int[] $tasks |
169
|
|
|
* @return array |
170
|
|
|
*/ |
171
|
|
View Code Duplication |
function loadTasks($tasks) |
|
|
|
|
172
|
|
|
{ |
173
|
|
|
$db = database(); |
174
|
|
|
|
175
|
|
|
$request = $db->query('', ' |
176
|
|
|
SELECT id_task, task |
177
|
|
|
FROM {db_prefix}scheduled_tasks |
178
|
|
|
WHERE id_task IN ({array_int:tasks}) |
179
|
|
|
LIMIT ' . count($tasks), |
180
|
|
|
array( |
181
|
|
|
'tasks' => $tasks, |
182
|
|
|
) |
183
|
|
|
); |
184
|
|
|
$task = array(); |
185
|
|
|
while ($row = $db->fetch_assoc($request)) |
186
|
|
|
$task[$row['id_task']] = $row['task']; |
187
|
|
|
$db->free_result($request); |
188
|
|
|
|
189
|
|
|
return $task; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* Logs a task. |
194
|
|
|
* |
195
|
|
|
* @package ScheduledTasks |
196
|
|
|
* @param int $id_log the id of the log entry of the task just run. If empty it is considered a new log entry |
197
|
|
|
* @param int $task_id the id of the task run (from the table scheduled_tasks) |
198
|
|
|
* @param int|null $total_time How long the task took to finish. If NULL (default value) -1 will be used |
199
|
|
|
* @return int the id_log value |
200
|
|
|
*/ |
201
|
|
|
function logTask($id_log, $task_id, $total_time = null) |
202
|
|
|
{ |
203
|
4 |
|
$db = database(); |
204
|
|
|
|
205
|
4 |
|
if (empty($id_log)) |
206
|
4 |
|
{ |
207
|
4 |
|
$db->insert('', |
208
|
4 |
|
'{db_prefix}log_scheduled_tasks', |
209
|
4 |
|
array('id_task' => 'int', 'time_run' => 'int', 'time_taken' => 'float'), |
210
|
4 |
|
array($task_id, time(), $total_time === null ? -1 : $total_time), |
211
|
4 |
|
array('id_task') |
212
|
4 |
|
); |
213
|
|
|
|
214
|
4 |
|
return $db->insert_id('{db_prefix}log_scheduled_tasks', 'id_log'); |
215
|
|
|
} |
216
|
|
|
else |
217
|
|
|
{ |
218
|
4 |
|
$db->query('', ' |
219
|
|
|
UPDATE {db_prefix}log_scheduled_tasks |
220
|
|
|
SET time_taken = {float:time_taken} |
221
|
4 |
|
WHERE id_log = {int:id_log}', |
222
|
|
|
array( |
223
|
4 |
|
'time_taken' => $total_time, |
224
|
4 |
|
'id_log' => $id_log, |
225
|
|
|
) |
226
|
4 |
|
); |
227
|
|
|
|
228
|
4 |
|
return $id_log; |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* All the scheduled tasks associated with the id passed to the function are |
234
|
|
|
* enabled, while the remaining are disabled |
235
|
|
|
* |
236
|
|
|
* @package ScheduledTasks |
237
|
|
|
* @param int[] $enablers array od task IDs |
238
|
|
|
*/ |
239
|
|
|
function updateTaskStatus($enablers) |
240
|
|
|
{ |
241
|
|
|
$db = database(); |
242
|
|
|
|
243
|
|
|
$db->query('', ' |
244
|
|
|
UPDATE {db_prefix}scheduled_tasks |
245
|
|
|
SET disabled = CASE WHEN id_task IN ({array_int:id_task_enable}) THEN 0 ELSE 1 END', |
246
|
|
|
array( |
247
|
|
|
'id_task_enable' => $enablers, |
248
|
|
|
) |
249
|
|
|
); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
/** |
253
|
|
|
* Sets the task status to enabled / disabled by task name (i.e. function) |
254
|
|
|
* |
255
|
|
|
* @package ScheduledTasks |
256
|
|
|
* @param string $enabler the name (the function) of a task |
257
|
|
|
* @param bool $enable is if the tasks should be enabled or disabled |
258
|
|
|
*/ |
259
|
|
|
function toggleTaskStatusByName($enabler, $enable = true) |
260
|
|
|
{ |
261
|
|
|
$db = database(); |
262
|
|
|
|
263
|
|
|
$db->query('', ' |
264
|
|
|
UPDATE {db_prefix}scheduled_tasks |
265
|
|
|
SET disabled = {int:status} |
266
|
|
|
WHERE task = {string:task_enable}', |
267
|
|
|
array( |
268
|
|
|
'task_enable' => $enabler, |
269
|
|
|
'status' => $enable ? 0 : 1, |
270
|
|
|
) |
271
|
|
|
); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Update the properties of a scheduled task. |
276
|
|
|
* |
277
|
|
|
* @package ScheduledTasks |
278
|
|
|
* @param int $id_task |
279
|
|
|
* @param int|null $disabled |
280
|
|
|
* @param int|null $offset |
281
|
|
|
* @param int|null $interval |
282
|
|
|
* @param string|null $unit |
283
|
|
|
*/ |
284
|
|
|
function updateTask($id_task, $disabled = null, $offset = null, $interval = null, $unit = null) |
285
|
|
|
{ |
286
|
|
|
$db = database(); |
287
|
|
|
|
288
|
|
|
$sets = array( |
289
|
|
|
'disabled' => 'disabled = {int:disabled}', |
290
|
|
|
'offset' => 'time_offset = {int:time_offset}', |
291
|
|
|
'interval' => 'time_regularity = {int:time_regularity}', |
292
|
|
|
'unit' => 'time_unit = {string:time_unit}', |
293
|
|
|
); |
294
|
|
|
|
295
|
|
|
$updates = array(); |
296
|
|
|
foreach ($sets as $key => $set) |
297
|
|
|
if (isset($$key)) |
298
|
|
|
$updates[] = $set; |
299
|
|
|
|
300
|
|
|
$db->query('', ' |
301
|
|
|
UPDATE {db_prefix}scheduled_tasks |
302
|
|
|
SET ' . (implode(', |
303
|
|
|
', $updates)) . ' |
304
|
|
|
WHERE id_task = {int:id_task}', |
305
|
|
|
array( |
306
|
|
|
'disabled' => $disabled, |
307
|
|
|
'time_offset' => $offset, |
308
|
|
|
'time_regularity' => $interval, |
309
|
|
|
'id_task' => $id_task, |
310
|
|
|
'time_unit' => $unit, |
311
|
|
|
) |
312
|
|
|
); |
313
|
|
|
} |
314
|
|
|
|
315
|
|
|
/** |
316
|
|
|
* Loads the details from a given task. |
317
|
|
|
* |
318
|
|
|
* @package ScheduledTasks |
319
|
|
|
* |
320
|
|
|
* @param int $id_task |
321
|
|
|
* |
322
|
|
|
* @return array |
323
|
|
|
* @throws Elk_Exception no_access |
324
|
|
|
*/ |
325
|
|
|
function loadTaskDetails($id_task) |
326
|
|
|
{ |
327
|
|
|
global $txt; |
328
|
|
|
|
329
|
|
|
$db = database(); |
330
|
|
|
|
331
|
|
|
$task = array(); |
332
|
|
|
|
333
|
|
|
$request = $db->query('', ' |
334
|
|
|
SELECT id_task, next_time, time_offset, time_regularity, time_unit, disabled, task |
335
|
|
|
FROM {db_prefix}scheduled_tasks |
336
|
|
|
WHERE id_task = {int:id_task}', |
337
|
|
|
array( |
338
|
|
|
'id_task' => $id_task, |
339
|
|
|
) |
340
|
|
|
); |
341
|
|
|
// Should never, ever, happen! |
342
|
|
|
if ($db->num_rows($request) == 0) |
343
|
|
|
throw new Elk_Exception('no_access', false); |
344
|
|
|
while ($row = $db->fetch_assoc($request)) |
345
|
|
|
{ |
346
|
|
|
$task = array( |
347
|
|
|
'id' => $row['id_task'], |
348
|
|
|
'function' => $row['task'], |
349
|
|
|
'name' => isset($txt['scheduled_task_' . $row['task']]) ? $txt['scheduled_task_' . $row['task']] : $row['task'], |
350
|
|
|
'desc' => isset($txt['scheduled_task_desc_' . $row['task']]) ? $txt['scheduled_task_desc_' . $row['task']] : '', |
351
|
|
|
'next_time' => $row['disabled'] ? $txt['scheduled_tasks_na'] : standardTime($row['next_time'] == 0 ? time() : $row['next_time'], true, 'server'), |
352
|
|
|
'disabled' => $row['disabled'], |
353
|
|
|
'offset' => $row['time_offset'], |
354
|
|
|
'regularity' => $row['time_regularity'], |
355
|
|
|
'offset_formatted' => date('H:i', $row['time_offset']), |
356
|
|
|
'unit' => $row['time_unit'], |
357
|
|
|
); |
358
|
|
|
} |
359
|
|
|
$db->free_result($request); |
360
|
|
|
|
361
|
|
|
return $task; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Returns an array of registered scheduled tasks. |
366
|
|
|
* |
367
|
|
|
* - Used also by createList() callbacks. |
368
|
|
|
* |
369
|
|
|
* @package ScheduledTasks |
370
|
|
|
* @return array |
371
|
|
|
*/ |
372
|
|
|
function scheduledTasks() |
373
|
|
|
{ |
374
|
|
|
global $txt; |
375
|
|
|
|
376
|
|
|
$db = database(); |
377
|
|
|
|
378
|
|
|
$request = $db->query('', ' |
379
|
|
|
SELECT id_task, next_time, time_offset, time_regularity, time_unit, disabled, task |
380
|
|
|
FROM {db_prefix}scheduled_tasks', |
381
|
|
|
array( |
382
|
|
|
) |
383
|
|
|
); |
384
|
|
|
$known_tasks = array(); |
385
|
|
|
while ($row = $db->fetch_assoc($request)) |
386
|
|
|
{ |
387
|
|
|
// Find the next for regularity - don't offset as it's always server time! |
388
|
|
|
$offset = sprintf($txt['scheduled_task_reg_starting'], date('H:i', $row['time_offset'])); |
389
|
|
|
$repeating = sprintf($txt['scheduled_task_reg_repeating'], $row['time_regularity'], $txt['scheduled_task_reg_unit_' . $row['time_unit']]); |
390
|
|
|
|
391
|
|
|
$known_tasks[] = array( |
392
|
|
|
'id' => $row['id_task'], |
393
|
|
|
'function' => $row['task'], |
394
|
|
|
'name' => isset($txt['scheduled_task_' . $row['task']]) ? $txt['scheduled_task_' . $row['task']] : $row['task'], |
395
|
|
|
'desc' => isset($txt['scheduled_task_desc_' . $row['task']]) ? $txt['scheduled_task_desc_' . $row['task']] : '', |
396
|
|
|
'next_time' => $row['disabled'] ? $txt['scheduled_tasks_na'] : standardTime(($row['next_time'] == 0 ? time() : $row['next_time']), true, 'server'), |
397
|
|
|
'disabled' => $row['disabled'], |
398
|
|
|
'checked_state' => $row['disabled'] ? '' : 'checked="checked"', |
399
|
|
|
'regularity' => $offset . ', ' . $repeating, |
400
|
|
|
); |
401
|
|
|
} |
402
|
|
|
$db->free_result($request); |
403
|
|
|
|
404
|
|
|
return $known_tasks; |
405
|
|
|
} |
406
|
|
|
|
407
|
|
|
/** |
408
|
|
|
* Return task log entries, within the passed limits. |
409
|
|
|
* |
410
|
|
|
* - Used by createList() callbacks. |
411
|
|
|
* |
412
|
|
|
* @package ScheduledTasks |
413
|
|
|
* @param int $start The item to start with (for pagination purposes) |
414
|
|
|
* @param int $items_per_page The number of items to show per page |
415
|
|
|
* @param string $sort A string indicating how to sort the results |
416
|
|
|
* |
417
|
|
|
* @return array |
418
|
|
|
*/ |
419
|
|
|
function getTaskLogEntries($start, $items_per_page, $sort) |
420
|
|
|
{ |
421
|
|
|
global $txt; |
422
|
|
|
|
423
|
|
|
$db = database(); |
424
|
|
|
|
425
|
|
|
$request = $db->query('', ' |
426
|
|
|
SELECT lst.id_log, lst.id_task, lst.time_run, lst.time_taken, st.task |
427
|
|
|
FROM {db_prefix}log_scheduled_tasks AS lst |
428
|
|
|
INNER JOIN {db_prefix}scheduled_tasks AS st ON (st.id_task = lst.id_task) |
429
|
|
|
ORDER BY ' . $sort . ' |
430
|
|
|
LIMIT ' . $start . ', ' . $items_per_page, |
431
|
|
|
array( |
432
|
|
|
) |
433
|
|
|
); |
434
|
|
|
$log_entries = array(); |
435
|
|
|
while ($row = $db->fetch_assoc($request)) |
436
|
|
|
$log_entries[] = array( |
437
|
|
|
'id' => $row['id_log'], |
438
|
|
|
'name' => isset($txt['scheduled_task_' . $row['task']]) ? $txt['scheduled_task_' . $row['task']] : $row['task'], |
439
|
|
|
'time_run' => $row['time_run'], |
440
|
|
|
// -1 means failed task, but in order to look better in the UI we switch it to 0 |
441
|
|
|
'time_taken' => $row['time_taken'] == -1 ? 0 : $row['time_taken'], |
442
|
|
|
'task_completed' => $row['time_taken'] != -1, |
443
|
|
|
); |
444
|
|
|
$db->free_result($request); |
445
|
|
|
|
446
|
|
|
return $log_entries; |
447
|
|
|
} |
448
|
|
|
|
449
|
|
|
/** |
450
|
|
|
* Return the number of task log entries. |
451
|
|
|
* |
452
|
|
|
* - Used by createList() callbacks. |
453
|
|
|
* |
454
|
|
|
* @package ScheduledTasks |
455
|
|
|
* @return int |
456
|
|
|
*/ |
457
|
|
|
function countTaskLogEntries() |
458
|
|
|
{ |
459
|
|
|
$db = database(); |
460
|
|
|
|
461
|
|
|
$request = $db->query('', ' |
462
|
|
|
SELECT COUNT(*) |
463
|
|
|
FROM {db_prefix}log_scheduled_tasks', |
464
|
|
|
array( |
465
|
|
|
) |
466
|
|
|
); |
467
|
|
|
list ($num_entries) = $db->fetch_row($request); |
468
|
|
|
$db->free_result($request); |
469
|
|
|
|
470
|
|
|
return $num_entries; |
471
|
|
|
} |
472
|
|
|
|
473
|
|
|
/** |
474
|
|
|
* Empty the scheduled tasks log. |
475
|
|
|
*/ |
476
|
|
|
function emptyTaskLog() |
477
|
|
|
{ |
478
|
|
|
$db = database(); |
479
|
|
|
|
480
|
|
|
$db->query('truncate_table', ' |
481
|
|
|
TRUNCATE {db_prefix}log_scheduled_tasks', |
482
|
|
|
array( |
483
|
|
|
) |
484
|
|
|
); |
485
|
|
|
} |
486
|
|
|
|
487
|
|
|
/** |
488
|
|
|
* Process the next tasks, one by one, and update the results. |
489
|
|
|
* |
490
|
|
|
* @package ScheduledTasks |
491
|
|
|
* @param int $ts = 0 |
492
|
|
|
*/ |
493
|
|
|
function processNextTasks($ts = 0) |
494
|
|
|
{ |
495
|
4 |
|
$db = database(); |
496
|
|
|
|
497
|
|
|
// Select the next task to do. |
498
|
4 |
|
$request = $db->query('', ' |
499
|
|
|
SELECT id_task, task, next_time, time_offset, time_regularity, time_unit |
500
|
|
|
FROM {db_prefix}scheduled_tasks |
501
|
|
|
WHERE disabled = {int:not_disabled} |
502
|
|
|
AND next_time <= {int:current_time} |
503
|
|
|
ORDER BY next_time ASC |
504
|
4 |
|
LIMIT 1', |
505
|
|
|
array( |
506
|
4 |
|
'not_disabled' => 0, |
507
|
4 |
|
'current_time' => time(), |
508
|
|
|
) |
509
|
4 |
|
); |
510
|
4 |
|
if ($db->num_rows($request) != 0) |
511
|
4 |
|
{ |
512
|
|
|
// The two important things really... |
513
|
4 |
|
$row = $db->fetch_assoc($request); |
514
|
|
|
|
515
|
|
|
// When should this next be run? |
516
|
4 |
|
$next_time = next_time($row['time_regularity'], $row['time_unit'], $row['time_offset']); |
517
|
|
|
|
518
|
|
|
// How long in seconds is the gap? |
519
|
4 |
|
$duration = $row['time_regularity']; |
520
|
4 |
|
if ($row['time_unit'] == 'm') |
521
|
4 |
|
$duration *= 60; |
522
|
4 |
|
elseif ($row['time_unit'] == 'h') |
523
|
|
|
$duration *= 3600; |
524
|
4 |
|
elseif ($row['time_unit'] == 'd') |
525
|
2 |
|
$duration *= 86400; |
526
|
2 |
|
elseif ($row['time_unit'] == 'w') |
527
|
2 |
|
$duration *= 604800; |
528
|
|
|
|
529
|
|
|
// If we were really late running this task actually skip the next one. |
530
|
4 |
|
if (time() + ($duration / 2) > $next_time) |
531
|
4 |
|
$next_time += $duration; |
532
|
|
|
|
533
|
|
|
// Update it now, so no others run this! |
534
|
4 |
|
$db->query('', ' |
535
|
|
|
UPDATE {db_prefix}scheduled_tasks |
536
|
|
|
SET next_time = {int:next_time} |
537
|
|
|
WHERE id_task = {int:id_task} |
538
|
4 |
|
AND next_time = {int:current_next_time}', |
539
|
|
|
array( |
540
|
4 |
|
'next_time' => $next_time, |
541
|
4 |
|
'id_task' => $row['id_task'], |
542
|
4 |
|
'current_next_time' => $row['next_time'], |
543
|
|
|
) |
544
|
4 |
|
); |
545
|
4 |
|
$affected_rows = $db->affected_rows(); |
546
|
|
|
|
547
|
|
|
// Do also some timestamp checking, |
548
|
|
|
// and do this only if we updated it before. |
549
|
4 |
|
if ((empty($ts) || $ts == $row['next_time']) && $affected_rows) |
550
|
4 |
|
{ |
551
|
4 |
|
ignore_user_abort(true); |
552
|
4 |
|
run_this_task($row['id_task'], $row['task']); |
553
|
4 |
|
} |
554
|
|
|
|
555
|
4 |
|
} |
556
|
4 |
|
$db->free_result($request); |
557
|
4 |
|
} |
558
|
|
|
|
559
|
|
|
/** |
560
|
|
|
* Calls the supplied task_name so that it is executed. |
561
|
|
|
* |
562
|
|
|
* - Logs that the task was executed with success or if it failed |
563
|
|
|
* |
564
|
|
|
* @package ScheduledTasks |
565
|
|
|
* @param int $id_task specific id of the task to run, used for logging |
566
|
|
|
* @param string $task_name name of the task, class name, function name, method in ScheduledTask.class |
567
|
|
|
*/ |
568
|
|
|
function run_this_task($id_task, $task_name) |
569
|
|
|
{ |
570
|
4 |
|
global $time_start, $modSettings; |
571
|
|
|
|
572
|
4 |
|
Elk_Autoloader::instance()->register(SUBSDIR . '/ScheduledTask', '\\ElkArte\\sources\\subs\\ScheduledTask'); |
573
|
|
|
|
574
|
|
|
// Let's start logging the task and saying we failed it |
575
|
4 |
|
$log_task_id = logTask(0, $id_task); |
576
|
|
|
|
577
|
4 |
|
$class = '\\ElkArte\\sources\\subs\\ScheduledTask\\' . implode('_', array_map('ucfirst', explode('_', $task_name))); |
578
|
|
|
|
579
|
4 |
|
if (class_exists($class)) |
580
|
4 |
|
{ |
581
|
4 |
|
$task = new $class(); |
582
|
|
|
|
583
|
4 |
|
$completed = $task->run(); |
584
|
4 |
|
} |
585
|
|
|
else |
586
|
|
|
$completed = run_this_task_compat($task_name); |
|
|
|
|
587
|
|
|
|
588
|
4 |
|
$scheduleTaskImmediate = !empty($modSettings['scheduleTaskImmediate']) ? Util::unserialize($modSettings['scheduleTaskImmediate']) : array(); |
589
|
|
|
// Log that we did it ;) |
590
|
|
|
if ($completed) |
591
|
4 |
|
{ |
592
|
|
|
// Taking care of scheduleTaskImmediate having a maximum of 10 "fast" executions |
593
|
4 |
|
if (!empty($scheduleTaskImmediate) && isset($scheduleTaskImmediate[$task_name])) |
594
|
4 |
|
{ |
595
|
|
|
$scheduleTaskImmediate[$task_name]++; |
596
|
|
|
|
597
|
|
|
if ($scheduleTaskImmediate[$task_name] > 9) |
598
|
|
|
removeScheduleTaskImmediate($task_name, false); |
599
|
|
|
else |
600
|
|
|
updateSettings(array('scheduleTaskImmediate' => serialize($scheduleTaskImmediate))); |
601
|
|
|
} |
602
|
|
|
|
603
|
4 |
|
$total_time = round(microtime(true) - $time_start, 3); |
604
|
|
|
|
605
|
|
|
// If the task ended successfully, then log the proper time taken to complete |
606
|
4 |
|
logTask($log_task_id, $id_task, $total_time); |
607
|
4 |
|
} |
608
|
4 |
|
} |
609
|
|
|
|
610
|
|
|
/** |
611
|
|
|
* 1.0 compatibility function, used to maintain compatibility with naming |
612
|
|
|
* scheme used in ElkArte 1.0 |
613
|
|
|
* |
614
|
|
|
* @package ScheduledTasks |
615
|
|
|
* @param string $task_name name of the task, class name, function name, method in ScheduledTask.class |
616
|
|
|
* @return mixed bool |
617
|
|
|
* @deprecated since 1.1 - Deprecated in favour of naming scheme |
618
|
|
|
*/ |
619
|
|
|
function run_this_task_compat($task_name) |
620
|
|
|
{ |
621
|
|
|
$completed = false; |
622
|
|
|
|
623
|
|
|
// The method must exist in ScheduledTask class, or we are wasting our time. |
624
|
|
|
// Actually for extendability sake, we need to have other ways, so: |
625
|
|
|
// A simple procedural function? |
626
|
|
|
if (strpos($task_name, '::') === false && function_exists($task_name)) |
627
|
|
|
{ |
628
|
|
|
$method = $task_name; |
629
|
|
|
|
630
|
|
|
// Do the task... |
631
|
|
|
$completed = $method(); |
632
|
|
|
} |
633
|
|
|
// It may be a class (no static, sorry) |
634
|
|
|
elseif (strpos($task_name, '::') !== false) |
635
|
|
|
{ |
636
|
|
|
$call = explode('::', $task_name); |
637
|
|
|
$task_object = new $call[0]; |
638
|
|
|
$method = $call[1]; |
639
|
|
|
|
640
|
|
|
if (method_exists($task_object, $method)) |
641
|
|
|
{ |
642
|
|
|
// Try to stop a timeout, this would be bad... |
643
|
|
|
detectServer()->setTimeLimit(300); |
644
|
|
|
|
645
|
|
|
// Do the task... |
646
|
|
|
$completed = $task_object->{$method}(); |
647
|
|
|
} |
648
|
|
|
} |
649
|
|
|
|
650
|
|
|
return $completed; |
651
|
|
|
} |
652
|
|
|
|
653
|
|
|
/** |
654
|
|
|
* Retrieve info if there's any next task scheduled and when. |
655
|
|
|
* |
656
|
|
|
* @package ScheduledTasks |
657
|
|
|
* @return mixed int|false |
658
|
|
|
*/ |
659
|
|
View Code Duplication |
function nextTime() |
|
|
|
|
660
|
|
|
{ |
661
|
4 |
|
$db = database(); |
662
|
|
|
|
663
|
|
|
// The next stored timestamp, is there any? |
664
|
4 |
|
$request = $db->query('', ' |
665
|
|
|
SELECT next_time |
666
|
|
|
FROM {db_prefix}scheduled_tasks |
667
|
|
|
WHERE disabled = {int:not_disabled} |
668
|
|
|
ORDER BY next_time ASC |
669
|
4 |
|
LIMIT 1', |
670
|
|
|
array( |
671
|
4 |
|
'not_disabled' => 0, |
672
|
|
|
) |
673
|
4 |
|
); |
674
|
|
|
// No new task scheduled? |
675
|
4 |
|
if ($db->num_rows($request) === 0) |
676
|
4 |
|
$result = false; |
677
|
|
|
else |
678
|
4 |
|
list ($result) = $db->fetch_row($request); |
679
|
|
|
|
680
|
4 |
|
$db->free_result($request); |
681
|
|
|
|
682
|
|
|
return $result; |
683
|
|
|
} |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.