ILIAS  release_8 Revision v8.23
class.ilCronJob.php
Go to the documentation of this file.
1 <?php
2 
19 declare(strict_types=1);
20 
21 abstract class ilCronJob
22 {
24  public const SCHEDULE_TYPE_DAILY = 1;
26  public const SCHEDULE_TYPE_IN_MINUTES = 2;
28  public const SCHEDULE_TYPE_IN_HOURS = 3;
30  public const SCHEDULE_TYPE_IN_DAYS = 4;
32  public const SCHEDULE_TYPE_WEEKLY = 5;
34  public const SCHEDULE_TYPE_MONTHLY = 6;
36  public const SCHEDULE_TYPE_QUARTERLY = 7;
38  public const SCHEDULE_TYPE_YEARLY = 8;
39 
40  protected ?int $schedule_type = null;
41  protected ?int $schedule_value = null;
42  protected ?Closure $date_time_provider = null;
43 
44  private function checkWeeklySchedule(DateTimeImmutable $last_run, DateTimeImmutable $now): bool
45  {
46  $last_year = (int) $last_run->format('Y');
47  $now_year = (int) $now->format('Y');
48 
49  if ($last_year > $now_year) {
50  // Should never happen, don't execute otherwise
51  return false;
52  }
53 
54  $last_week = $last_run->format('W');
55  $now_week = $now->format('W');
56 
57  if ($last_week !== $now_week) {
58  // Week differs, always execute the job
59  return true;
60  }
61 
62  // For all following cases, the week number is always identical
63 
64  $last_month = (int) $last_run->format('m');
65  $now_month = (int) $now->format('m');
66 
67  $is_within_same_week_in_same_year = ($last_year . '-' . $last_week) === ($now_year . '-' . $now_week);
68  if ($is_within_same_week_in_same_year) {
69  // Same week in same year, only execute if the month differs (2022-52 is valid for January and December)
70  return $last_month !== $now_month && $now->diff($last_run)->d > 7;
71  }
72 
73  if ($now_year - $last_year > 1) {
74  // Always execute if the difference of years is greater than 1
75  return true;
76  }
77 
78  // Execute for week number 52 in 2022 (last run) and week number 52 in December of 2022 (now), but not for week number 52 in January of 2022 (now)
79  return $last_month === $now_month;
80  }
81 
82  private function checkSchedule(?DateTimeImmutable $last_run, ?int $schedule_type, ?int $schedule_value): bool
83  {
84  if (null === $schedule_type) {
85  return false;
86  }
87 
88  if (null === $last_run) {
89  return true;
90  }
91 
92  if ($this->date_time_provider === null) {
93  $now = new DateTimeImmutable('@' . time(), new DateTimeZone(date_default_timezone_get()));
94  } else {
95  $now = ($this->date_time_provider)();
96  }
97 
98  switch ($schedule_type) {
99  case self::SCHEDULE_TYPE_DAILY:
100  $last = $last_run->format('Y-m-d');
101  $ref = $now->format('Y-m-d');
102  return ($last !== $ref);
103 
104  case self::SCHEDULE_TYPE_WEEKLY:
105  return $this->checkWeeklySchedule($last_run, $now);
106 
107  case self::SCHEDULE_TYPE_MONTHLY:
108  $last = $last_run->format('Y-n');
109  $ref = $now->format('Y-n');
110  return ($last !== $ref);
111 
112  case self::SCHEDULE_TYPE_QUARTERLY:
113  $last = $last_run->format('Y') . '-' . ceil(((int) $last_run->format('n')) / 3);
114  $ref = $now->format('Y') . '-' . ceil(((int) $now->format('n')) / 3);
115  return ($last !== $ref);
116 
117  case self::SCHEDULE_TYPE_YEARLY:
118  $last = $last_run->format('Y');
119  $ref = $now->format('Y');
120  return ($last !== $ref);
121 
122  case self::SCHEDULE_TYPE_IN_MINUTES:
123  $diff = floor(($now->getTimestamp() - $last_run->getTimestamp()) / 60);
124  return ($diff >= $schedule_value);
125 
126  case self::SCHEDULE_TYPE_IN_HOURS:
127  $diff = floor(($now->getTimestamp() - $last_run->getTimestamp()) / (60 * 60));
128  return ($diff >= $schedule_value);
129 
130  case self::SCHEDULE_TYPE_IN_DAYS:
131  $diff = floor(($now->getTimestamp() - $last_run->getTimestamp()) / (60 * 60 * 24));
132  return ($diff >= $schedule_value);
133  }
134 
135  return false;
136  }
137 
141  public function setDateTimeProvider(?Closure $date_time_provider): void
142  {
143  if ($date_time_provider !== null) {
144  $r = new ReflectionFunction($date_time_provider);
145  $return_type = $r->getReturnType();
146  if ($return_type !== null) {
147  $return_type = $return_type->getName();
148  }
149  $expected_type = DateTimeInterface::class;
150  if (!is_subclass_of($return_type, $expected_type)) {
151  throw new InvalidArgumentException(sprintf(
152  'The return type of the datetime provider must be of type %s',
153  $expected_type
154  ));
155  }
156 
157  $r = new ReflectionFunction($date_time_provider);
158  $parameters = $r->getParameters();
159  if ($parameters !== []) {
160  throw new InvalidArgumentException(
161  'The datetime provider must not define any parameters',
162  );
163  }
164  }
165 
166  $this->date_time_provider = $date_time_provider;
167  }
168 
169  public function isDue(
170  ?DateTimeImmutable $last_run,
171  ?int $schedule_type,
172  ?int $schedule_value,
173  bool $is_manually_executed = false
174  ): bool {
175  if ($is_manually_executed) {
176  return true;
177  }
178 
179  if (!$this->hasFlexibleSchedule()) {
180  $schedule_type = $this->getDefaultScheduleType();
181  $schedule_value = $this->getDefaultScheduleValue();
182  }
183 
184  return $this->checkSchedule($last_run, $schedule_type, $schedule_value);
185  }
186 
191  public function getScheduleType(): ?int
192  {
193  if ($this->schedule_type && $this->hasFlexibleSchedule()) {
194  return $this->schedule_type;
195  }
196 
197  return null;
198  }
199 
204  public function getScheduleValue(): ?int
205  {
206  if ($this->schedule_value && $this->hasFlexibleSchedule()) {
207  return $this->schedule_value;
208  }
209 
210  return null;
211  }
212 
218  public function setSchedule(?int $a_type, ?int $a_value): void
219  {
220  if (
221  $a_value &&
222  $this->hasFlexibleSchedule() &&
223  in_array($a_type, $this->getValidScheduleTypes(), true)
224  ) {
225  $this->schedule_type = $a_type;
226  $this->schedule_value = $a_value;
227  }
228  }
229 
234  public function getAllScheduleTypes(): array
235  {
236  return [
237  self::SCHEDULE_TYPE_DAILY,
238  self::SCHEDULE_TYPE_WEEKLY,
239  self::SCHEDULE_TYPE_MONTHLY,
240  self::SCHEDULE_TYPE_QUARTERLY,
241  self::SCHEDULE_TYPE_YEARLY,
242  self::SCHEDULE_TYPE_IN_MINUTES,
243  self::SCHEDULE_TYPE_IN_HOURS,
244  self::SCHEDULE_TYPE_IN_DAYS,
245  ];
246  }
247 
251  public function getScheduleTypesWithValues(): array
252  {
253  return [
254  self::SCHEDULE_TYPE_IN_MINUTES,
255  self::SCHEDULE_TYPE_IN_HOURS,
256  self::SCHEDULE_TYPE_IN_DAYS,
257  ];
258  }
259 
264  public function getValidScheduleTypes(): array
265  {
266  return $this->getAllScheduleTypes();
267  }
268 
269  public function isManuallyExecutable(): bool
270  {
271  return true;
272  }
273 
274  public function hasCustomSettings(): bool
275  {
276  return false;
277  }
278 
279  public function addCustomSettingsToForm(ilPropertyFormGUI $a_form): void
280  {
281  }
282 
283  public function saveCustomSettings(ilPropertyFormGUI $a_form): bool
284  {
285  return true;
286  }
287 
288  public function addToExternalSettingsForm(int $a_form_id, array &$a_fields, bool $a_is_active): void
289  {
290  }
291 
300  public function activationWasToggled(ilDBInterface $db, ilSetting $setting, bool $a_currently_active): void
301  {
302  }
303 
304  abstract public function getId(): string;
305 
306  abstract public function getTitle(): string;
307 
308  abstract public function getDescription(): string;
309 
313  abstract public function hasAutoActivation(): bool;
314 
315  abstract public function hasFlexibleSchedule(): bool;
316 
317  abstract public function getDefaultScheduleType(): int;
318 
319  abstract public function getDefaultScheduleValue(): ?int;
320 
321  abstract public function run(): ilCronJobResult;
322 }
activationWasToggled(ilDBInterface $db, ilSetting $setting, bool $a_currently_active)
Important: This method is (also) called from the setup process, where the constructor of an ilCronJob...
getValidScheduleTypes()
Returns a collection of all valid schedule types for a specific job.
setDateTimeProvider(?Closure $date_time_provider)
getAllScheduleTypes()
Get all available schedule types.
setSchedule(?int $a_type, ?int $a_value)
Update current schedule (if flexible)
const SCHEDULE_TYPE_IN_MINUTES
This will be replaced with an ENUM in ILIAS 9
getDescription()
const SCHEDULE_TYPE_MONTHLY
This will be replaced with an ENUM in ILIAS 9
const SCHEDULE_TYPE_WEEKLY
This will be replaced with an ENUM in ILIAS 9
addCustomSettingsToForm(ilPropertyFormGUI $a_form)
hasFlexibleSchedule()
const SCHEDULE_TYPE_IN_DAYS
This will be replaced with an ENUM in ILIAS 9
addToExternalSettingsForm(int $a_form_id, array &$a_fields, bool $a_is_active)
getScheduleTypesWithValues()
const SCHEDULE_TYPE_YEARLY
This will be replaced with an ENUM in ILIAS 9
getDefaultScheduleType()
Closure $date_time_provider
const SCHEDULE_TYPE_DAILY
This will be replaced with an ENUM in ILIAS 9
getDefaultScheduleValue()
saveCustomSettings(ilPropertyFormGUI $a_form)
const SCHEDULE_TYPE_QUARTERLY
This will be replaced with an ENUM in ILIAS 9
getScheduleValue()
Get current schedule value (if flexible)
const SCHEDULE_TYPE_IN_HOURS
This will be replaced with an ENUM in ILIAS 9
hasAutoActivation()
Is to be activated on "installation", does only work for ILIAS core cron jobs.
checkSchedule(?DateTimeImmutable $last_run, ?int $schedule_type, ?int $schedule_value)
getScheduleType()
Get current schedule type (if flexible)
isDue(?DateTimeImmutable $last_run, ?int $schedule_type, ?int $schedule_value, bool $is_manually_executed=false)
checkWeeklySchedule(DateTimeImmutable $last_run, DateTimeImmutable $now)