ILIAS  release_8 Revision v8.24
class.ilCronManagerImpl.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22
24{
30
31 public function __construct(
37 ) {
38 $this->cronRepository = $cronRepository;
39 $this->db = $db;
40 $this->settings = $settings;
41 $this->logger = $logger;
42 $this->clock_factory = $clock_factory;
43 }
44
45 private function getMicrotime(): float
46 {
47 return ((int) $this->clock_factory->system()->now()->format('Uu')) / 1000000;
48 }
49
50 public function runActiveJobs(ilObjUser $actor): void
51 {
52 $this->logger->info('CRON - batch start');
53
54 $ts = $this->clock_factory->system()->now()->getTimestamp();
55 $this->settings->set('last_cronjob_start_ts', (string) $ts);
56
57 $useRelativeDates = ilDatePresentation::useRelativeDates();
59 $this->logger->info(sprintf(
60 'Set last datetime to: %s',
62 ));
63 $this->logger->info(sprintf(
64 'Verification of last run datetime (read from database): %s',
66 new ilDateTime(ilSetting::_lookupValue('common', 'last_cronjob_start_ts'), IL_CAL_UNIX)
67 )
68 ));
70
71 // ilLink::_getStaticLink() should work in crons
72 if (!defined('ILIAS_HTTP_PATH')) {
73 define('ILIAS_HTTP_PATH', ilUtil::_getHttpPath());
74 }
75
76 // system
77 foreach ($this->cronRepository->getCronJobData(null, false) as $row) {
78 $job = $this->cronRepository->getJobInstanceById($row['job_id']);
79 if ($job instanceof ilCronJob) {
80 // #18411 - we are NOT using the initial job data as it might be outdated at this point
81 $this->runJob($job, $actor);
82 }
83 }
84
85 // plugins
86 foreach ($this->cronRepository->getPluginJobs(true) as $item) {
87 // #18411 - we are NOT using the initial job data as it might be outdated at this point
88 $this->runJob($item[0], $actor);
89 }
90
91 $this->logger->info('CRON - batch end');
92 }
93
94 public function runJobManual(string $jobId, ilObjUser $actor): bool
95 {
96 $result = false;
97
98 $this->logger->info('CRON - manual start (' . $jobId . ')');
99
100 $job = $this->cronRepository->getJobInstanceById($jobId);
101 if ($job instanceof ilCronJob) {
102 if ($job->isManuallyExecutable()) {
103 $result = $this->runJob($job, $actor, null, true);
104 } else {
105 $this->logger->info('CRON - job ' . $jobId . ' is not intended to be executed manually');
106 }
107 } else {
108 $this->logger->info('CRON - job ' . $jobId . ' seems invalid or is inactive');
109 }
110
111 $this->logger->info('CRON - manual end (' . $jobId . ')');
112
113 return $result;
114 }
115
125 private function runJob(ilCronJob $job, ilObjUser $actor, ?array $jobData = null, bool $isManualExecution = false): bool
126 {
127 $did_run = false;
128
129 if (null === $jobData) {
130 // aquire "fresh" job (status) data
131 $jobsData = $this->cronRepository->getCronJobData($job->getId());
132 $jobData = array_pop($jobsData);
133 }
134
135 $job->setDateTimeProvider(function (): DateTimeImmutable {
136 return $this->clock_factory->system()->now();
137 });
138
139 // already running?
140 if ($jobData['alive_ts']) {
141 $this->logger->info('CRON - job ' . $jobData['job_id'] . ' still running');
142
143 $cut = 60 * 60 * 3;
144
145 // is running (and has not pinged) for 3 hours straight, we assume it crashed
146 if ($this->clock_factory->system()->now()->getTimestamp() - ((int) $jobData['alive_ts']) > $cut) {
147 $this->cronRepository->updateRunInformation($jobData['job_id'], 0, 0);
148 $this->deactivateJob($job, $actor); // #13082
149
150 $result = new ilCronJobResult();
151 $result->setStatus(ilCronJobResult::STATUS_CRASHED);
152 $result->setCode(ilCronJobResult::CODE_SUPPOSED_CRASH);
153 $result->setMessage('Cron job deactivated because it has been inactive for 3 hours');
154
155 $this->cronRepository->updateJobResult(
156 $job,
157 $this->clock_factory->system()->now(),
158 $actor,
159 $result,
160 $isManualExecution
161 );
162
163 $this->logger->info('CRON - job ' . $jobData['job_id'] . ' deactivated (assumed crash)');
164 }
165 } // initiate run?
166 elseif ($job->isDue(
167 $jobData['job_result_ts'] ? (new DateTimeImmutable(
168 '@' . $jobData['job_result_ts']
169 ))->setTimezone($this->clock_factory->system()->now()->getTimezone()) : null,
170 $jobData['schedule_type'] ? (int) $jobData['schedule_type'] : null,
171 $jobData['schedule_value'] ? (int) $jobData['schedule_value'] : null,
172 $isManualExecution
173 )) {
174 $this->logger->info('CRON - job ' . $jobData['job_id'] . ' started');
175
176 $this->cronRepository->updateRunInformation(
177 $jobData['job_id'],
178 $this->clock_factory->system()->now()->getTimestamp(),
179 $this->clock_factory->system()->now()->getTimestamp()
180 );
181
182 $ts_in = $this->getMicrotime();
183 try {
184 $result = $job->run();
185 } catch (Throwable $e) {
186 $result = new ilCronJobResult();
187 $result->setStatus(ilCronJobResult::STATUS_CRASHED);
188 $result->setMessage(
189 ilStr::subStr(sprintf('Exception: %s / %s', $e->getMessage(), $e->getTraceAsString()), 0, 400)
190 );
191
192 $this->logger->error($e->getMessage());
193 $this->logger->error($e->getTraceAsString());
194 } finally {
195 $ts_dur = $this->getMicrotime() - $ts_in;
196 }
197
198 if ($result->getStatus() === ilCronJobResult::STATUS_INVALID_CONFIGURATION) {
199 $this->deactivateJob($job, $actor);
200 $this->logger->info('CRON - job ' . $jobData['job_id'] . ' invalid configuration');
201 } else {
202 // success!
203 $did_run = true;
204 }
205
206 $result->setDuration($ts_dur);
207
208 $this->cronRepository->updateJobResult(
209 $job,
210 $this->clock_factory->system()->now(),
211 $actor,
212 $result,
213 $isManualExecution
214 );
215 $this->cronRepository->updateRunInformation($jobData['job_id'], 0, 0);
216
217 $this->logger->info('CRON - job ' . $jobData['job_id'] . ' finished');
218 } else {
219 $this->logger->info('CRON - job ' . $jobData['job_id'] . ' returned status inactive');
220 }
221
222 return $did_run;
223 }
224
225 public function resetJob(ilCronJob $job, ilObjUser $actor): void
226 {
227 $result = new ilCronJobResult();
228 $result->setStatus(ilCronJobResult::STATUS_RESET);
229 $result->setCode(ilCronJobResult::CODE_MANUAL_RESET);
230 $result->setMessage('Cron job re-activated by admin');
231
232 $this->cronRepository->updateJobResult(
233 $job,
234 $this->clock_factory->system()->now(),
235 $actor,
236 $result,
237 true
238 );
239 $this->cronRepository->resetJob($job);
240
241 $this->activateJob($job, $actor, true);
242 }
243
244 public function activateJob(ilCronJob $job, ilObjUser $actor, bool $wasManuallyExecuted = false): void
245 {
246 $this->cronRepository->activateJob($job, $this->clock_factory->system()->now(), $actor, $wasManuallyExecuted);
247 $job->activationWasToggled($this->db, $this->settings, true);
248 }
249
250 public function deactivateJob(ilCronJob $job, ilObjUser $actor, bool $wasManuallyExecuted = false): void
251 {
252 $this->cronRepository->deactivateJob($job, $this->clock_factory->system()->now(), $actor, $wasManuallyExecuted);
253 $job->activationWasToggled($this->db, $this->settings, false);
254 }
255
256 public function isJobActive(string $jobId): bool
257 {
258 $jobs_data = $this->cronRepository->getCronJobData($jobId);
259
260 return $jobs_data !== [] && $jobs_data[0]['job_status'];
261 }
262
263 public function isJobInactive(string $jobId): bool
264 {
265 $jobs_data = $this->cronRepository->getCronJobData($jobId);
266
267 return $jobs_data !== [] && !((bool) $jobs_data[0]['job_status']);
268 }
269
270 public function ping(string $jobId): void
271 {
272 $this->db->manipulateF(
273 'UPDATE cron_job SET alive_ts = %s WHERE job_id = %s',
274 ['integer', 'text'],
275 [$this->clock_factory->system()->now()->getTimestamp(), $jobId]
276 );
277 }
278}
const IL_CAL_UNIX
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...
setDateTimeProvider(?Closure $date_time_provider)
isDue(?DateTimeImmutable $last_run, ?int $schedule_type, ?int $schedule_value, bool $is_manually_executed=false)
activateJob(ilCronJob $job, ilObjUser $actor, bool $wasManuallyExecuted=false)
ilCronJobRepository $cronRepository
runJobManual(string $jobId, ilObjUser $actor)
resetJob(ilCronJob $job, ilObjUser $actor)
deactivateJob(ilCronJob $job, ilObjUser $actor, bool $wasManuallyExecuted=false)
runActiveJobs(ilObjUser $actor)
__construct(ilCronJobRepository $cronRepository, ilDBInterface $db, ilSetting $settings, ilLogger $logger, ClockFactory $clock_factory)
runJob(ilCronJob $job, ilObjUser $actor, ?array $jobData=null, bool $isManualExecution=false)
Run single cron job (internal)
static setUseRelativeDates(bool $a_status)
set use relative dates
static formatDate(ilDateTime $date, bool $a_skip_day=false, bool $a_include_wd=false, bool $include_seconds=false)
@classDescription Date and time handling
Component logger with individual log levels by component id.
User class.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
static _lookupValue(string $a_module, string $a_keyword)
static subStr(string $a_str, int $a_start, ?int $a_length=null)
Definition: class.ilStr.php:24
static _getHttpPath()
Interface ilDBInterface.