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