source: moodle/trunk/fuentes/lib/classes/task/scheduled_task.php @ 1331

Last change on this file since 1331 was 1331, checked in by jrpelegrina, 3 years ago

Updated to moodle 3.0.3

File size: 11.9 KB
Line 
1<?php
2// This file is part of Moodle - http://moodle.org/
3//
4// Moodle is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Moodle is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
17/**
18 * Scheduled task abstract class.
19 *
20 * @package    core
21 * @category   task
22 * @copyright  2013 Damyon Wiese
23 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25namespace core\task;
26
27/**
28 * Abstract class defining a scheduled task.
29 * @copyright  2013 Damyon Wiese
30 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31 */
32abstract class scheduled_task extends task_base {
33
34    /** Minimum minute value. */
35    const MINUTEMIN = 0;
36    /** Maximum minute value. */
37    const MINUTEMAX = 59;
38
39    /** Minimum hour value. */
40    const HOURMIN = 0;
41    /** Maximum hour value. */
42    const HOURMAX = 23;
43
44    /** Minimum dayofweek value. */
45    const DAYOFWEEKMIN = 0;
46    /** Maximum dayofweek value. */
47    const DAYOFWEEKMAX = 6;
48
49    /** @var string $hour - Pattern to work out the valid hours */
50    private $hour = '*';
51
52    /** @var string $minute - Pattern to work out the valid minutes */
53    private $minute = '*';
54
55    /** @var string $day - Pattern to work out the valid days */
56    private $day = '*';
57
58    /** @var string $month - Pattern to work out the valid months */
59    private $month = '*';
60
61    /** @var string $dayofweek - Pattern to work out the valid dayofweek */
62    private $dayofweek = '*';
63
64    /** @var int $lastruntime - When this task was last run */
65    private $lastruntime = 0;
66
67    /** @var boolean $customised - Has this task been changed from it's default schedule? */
68    private $customised = false;
69
70    /** @var int $disabled - Is this task disabled in cron? */
71    private $disabled = false;
72
73    /**
74     * Get the last run time for this scheduled task.
75     * @return int
76     */
77    public function get_last_run_time() {
78        return $this->lastruntime;
79    }
80
81    /**
82     * Set the last run time for this scheduled task.
83     * @param int $lastruntime
84     */
85    public function set_last_run_time($lastruntime) {
86        $this->lastruntime = $lastruntime;
87    }
88
89    /**
90     * Has this task been changed from it's default config?
91     * @return bool
92     */
93    public function is_customised() {
94        return $this->customised;
95    }
96
97    /**
98     * Has this task been changed from it's default config?
99     * @param bool
100     */
101    public function set_customised($customised) {
102        $this->customised = $customised;
103    }
104
105    /**
106     * Setter for $minute. Accepts a special 'R' value
107     * which will be translated to a random minute.
108     * @param string $minute
109     */
110    public function set_minute($minute) {
111        if ($minute === 'R') {
112            $minute = mt_rand(self::HOURMIN, self::HOURMAX);
113        }
114        $this->minute = $minute;
115    }
116
117    /**
118     * Getter for $minute.
119     * @return string
120     */
121    public function get_minute() {
122        return $this->minute;
123    }
124
125    /**
126     * Setter for $hour. Accepts a special 'R' value
127     * which will be translated to a random hour.
128     * @param string $hour
129     */
130    public function set_hour($hour) {
131        if ($hour === 'R') {
132            $hour = mt_rand(self::HOURMIN, self::HOURMAX);
133        }
134        $this->hour = $hour;
135    }
136
137    /**
138     * Getter for $hour.
139     * @return string
140     */
141    public function get_hour() {
142        return $this->hour;
143    }
144
145    /**
146     * Setter for $month.
147     * @param string $month
148     */
149    public function set_month($month) {
150        $this->month = $month;
151    }
152
153    /**
154     * Getter for $month.
155     * @return string
156     */
157    public function get_month() {
158        return $this->month;
159    }
160
161    /**
162     * Setter for $day.
163     * @param string $day
164     */
165    public function set_day($day) {
166        $this->day = $day;
167    }
168
169    /**
170     * Getter for $day.
171     * @return string
172     */
173    public function get_day() {
174        return $this->day;
175    }
176
177    /**
178     * Setter for $dayofweek.
179     * @param string $dayofweek
180     */
181    public function set_day_of_week($dayofweek) {
182        if ($dayofweek === 'R') {
183            $dayofweek = mt_rand(self::DAYOFWEEKMIN, self::DAYOFWEEKMAX);
184        }
185        $this->dayofweek = $dayofweek;
186    }
187
188    /**
189     * Getter for $dayofweek.
190     * @return string
191     */
192    public function get_day_of_week() {
193        return $this->dayofweek;
194    }
195
196    /**
197     * Setter for $disabled.
198     * @param bool $disabled
199     */
200    public function set_disabled($disabled) {
201        $this->disabled = (bool)$disabled;
202    }
203
204    /**
205     * Getter for $disabled.
206     * @return bool
207     */
208    public function get_disabled() {
209        return $this->disabled;
210    }
211
212    /**
213     * Override this function if you want this scheduled task to run, even if the component is disabled.
214     *
215     * @return bool
216     */
217    public function get_run_if_component_disabled() {
218        return false;
219    }
220
221    /**
222     * Take a cron field definition and return an array of valid numbers with the range min-max.
223     *
224     * @param string $field - The field definition.
225     * @param int $min - The minimum allowable value.
226     * @param int $max - The maximum allowable value.
227     * @return array(int)
228     */
229    public function eval_cron_field($field, $min, $max) {
230        // Cleanse the input.
231        $field = trim($field);
232
233        // Format for a field is:
234        // <fieldlist> := <range>(/<step>)(,<fieldlist>)
235        // <step>  := int
236        // <range> := <any>|<int>|<min-max>
237        // <any>   := *
238        // <min-max> := int-int
239        // End of format BNF.
240
241        // This function is complicated but is covered by unit tests.
242        $range = array();
243
244        $matches = array();
245        preg_match_all('@[0-9]+|\*|,|/|-@', $field, $matches);
246
247        $last = 0;
248        $inrange = false;
249        $instep = false;
250
251        foreach ($matches[0] as $match) {
252            if ($match == '*') {
253                array_push($range, range($min, $max));
254            } else if ($match == '/') {
255                $instep = true;
256            } else if ($match == '-') {
257                $inrange = true;
258            } else if (is_numeric($match)) {
259                if ($instep) {
260                    $i = 0;
261                    for ($i = 0; $i < count($range[count($range) - 1]); $i++) {
262                        if (($i) % $match != 0) {
263                            $range[count($range) - 1][$i] = -1;
264                        }
265                    }
266                    $inrange = false;
267                } else if ($inrange) {
268                    if (count($range)) {
269                        $range[count($range) - 1] = range($last, $match);
270                    }
271                    $inrange = false;
272                } else {
273                    if ($match >= $min && $match <= $max) {
274                        array_push($range, $match);
275                    }
276                    $last = $match;
277                }
278            }
279        }
280
281        // Flatten the result.
282        $result = array();
283        foreach ($range as $r) {
284            if (is_array($r)) {
285                foreach ($r as $rr) {
286                    if ($rr >= $min && $rr <= $max) {
287                        $result[$rr] = 1;
288                    }
289                }
290            } else if (is_numeric($r)) {
291                if ($r >= $min && $r <= $max) {
292                    $result[$r] = 1;
293                }
294            }
295        }
296        $result = array_keys($result);
297        sort($result, SORT_NUMERIC);
298        return $result;
299    }
300
301    /**
302     * Assuming $list is an ordered list of items, this function returns the item
303     * in the list that is greater than or equal to the current value (or 0). If
304     * no value is greater than or equal, this will return the first valid item in the list.
305     * If list is empty, this function will return 0.
306     *
307     * @param int $current The current value
308     * @param int[] $list The list of valid items.
309     * @return int $next.
310     */
311    private function next_in_list($current, $list) {
312        foreach ($list as $l) {
313            if ($l >= $current) {
314                return $l;
315            }
316        }
317        if (count($list)) {
318            return $list[0];
319        }
320
321        return 0;
322    }
323
324    /**
325     * Calculate when this task should next be run based on the schedule.
326     * @return int $nextruntime.
327     */
328    public function get_next_scheduled_time() {
329        global $CFG;
330
331        $validminutes = $this->eval_cron_field($this->minute, self::MINUTEMIN, self::MINUTEMAX);
332        $validhours = $this->eval_cron_field($this->hour, self::HOURMIN, self::HOURMAX);
333
334        // We need to change to the server timezone before using php date() functions.
335        \core_date::set_default_server_timezone();
336
337        $daysinmonth = date("t");
338        $validdays = $this->eval_cron_field($this->day, 1, $daysinmonth);
339        $validdaysofweek = $this->eval_cron_field($this->dayofweek, 0, 7);
340        $validmonths = $this->eval_cron_field($this->month, 1, 12);
341        $nextvalidyear = date('Y');
342
343        $currentminute = date("i") + 1;
344        $currenthour = date("H");
345        $currentday = date("j");
346        $currentmonth = date("n");
347        $currentdayofweek = date("w");
348
349        $nextvalidminute = $this->next_in_list($currentminute, $validminutes);
350        if ($nextvalidminute < $currentminute) {
351            $currenthour += 1;
352        }
353        $nextvalidhour = $this->next_in_list($currenthour, $validhours);
354        if ($nextvalidhour < $currenthour) {
355            $currentdayofweek += 1;
356            $currentday += 1;
357        }
358        $nextvaliddayofmonth = $this->next_in_list($currentday, $validdays);
359        $nextvaliddayofweek = $this->next_in_list($currentdayofweek, $validdaysofweek);
360        $daysincrementbymonth = $nextvaliddayofmonth - $currentday;
361        if ($nextvaliddayofmonth < $currentday) {
362            $daysincrementbymonth += $daysinmonth;
363        }
364
365        $daysincrementbyweek = $nextvaliddayofweek - $currentdayofweek;
366        if ($nextvaliddayofweek < $currentdayofweek) {
367            $daysincrementbyweek += 7;
368        }
369
370        // Special handling for dayofmonth vs dayofweek:
371        // if either field is * - use the other field
372        // otherwise - choose the soonest (see man 5 cron).
373        if ($this->dayofweek == '*') {
374            $daysincrement = $daysincrementbymonth;
375        } else if ($this->day == '*') {
376            $daysincrement = $daysincrementbyweek;
377        } else {
378            // Take the smaller increment of days by month or week.
379            $daysincrement = $daysincrementbymonth;
380            if ($daysincrementbyweek < $daysincrementbymonth) {
381                $daysincrement = $daysincrementbyweek;
382            }
383        }
384
385        $nextvaliddayofmonth = $currentday + $daysincrement;
386        if ($nextvaliddayofmonth > $daysinmonth) {
387            $currentmonth += 1;
388            $nextvaliddayofmonth -= $daysinmonth;
389        }
390
391        $nextvalidmonth = $this->next_in_list($currentmonth, $validmonths);
392        if ($nextvalidmonth < $currentmonth) {
393            $nextvalidyear += 1;
394        }
395
396        // Work out the next valid time.
397        $nexttime = mktime($nextvalidhour,
398                           $nextvalidminute,
399                           0,
400                           $nextvalidmonth,
401                           $nextvaliddayofmonth,
402                           $nextvalidyear);
403
404        return $nexttime;
405    }
406
407    /**
408     * Get a descriptive name for this task (shown to admins).
409     *
410     * @return string
411     */
412    public abstract function get_name();
413
414}
Note: See TracBrowser for help on using the repository browser.