ILIAS  release_8 Revision v8.24
class.ilCronJobRepositoryImpl.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
22{
23 private const TYPE_PLUGINS = 'Plugins';
24
30
31 public function __construct(
37 ) {
38 $this->db = $db;
39 $this->setting = $setting;
40 $this->logger = $logger;
41 $this->componentRepository = $componentRepository;
42 $this->componentFactory = $componentFactory;
43 }
44
45 public function getJobInstanceById(string $id): ?ilCronJob
46 {
47 // plugin
48 if (strpos($id, 'pl__') === 0) {
49 $parts = explode('__', $id);
50 $pl_name = $parts[1];
51 $job_id = $parts[2];
52
53 foreach ($this->componentRepository->getPlugins() as $pl) {
54 if ($pl->getName() !== $pl_name || !$pl->isActive()) {
55 continue;
56 }
57
58 $plugin = $this->componentFactory->getPlugin($pl->getId());
59 if (!$plugin instanceof ilCronJobProvider) {
60 continue;
61 }
62
63 try {
64 $job = $plugin->getCronJobInstance($job_id);
65
66 // should never happen but who knows...
67 $jobs_data = $this->getCronJobData($job_id);
68 if ($jobs_data === []) {
69 // as job is not 'imported' from xml
70 $this->createDefaultEntry($job, $pl_name, self::TYPE_PLUGINS, '');
71 }
72
73 return $job;
74 } catch (OutOfBoundsException $e) {
75 // Maybe a job was removed from plugin, renamed etc.
76 }
77 break;
78 }
79 } else {
80 $jobs_data = $this->getCronJobData($id);
81 if ($jobs_data !== [] && $jobs_data[0]['job_id'] === $id) {
82 return $this->getJobInstance(
83 $jobs_data[0]['job_id'],
84 $jobs_data[0]['component'],
85 $jobs_data[0]['class']
86 );
87 }
88 }
89
90 $this->logger->info('CRON - job ' . $id . ' seems invalid or is inactive');
91
92 return null;
93 }
94
95 public function getJobInstance(
96 string $a_id,
97 string $a_component,
98 string $a_class,
99 bool $isCreationContext = false
100 ): ?ilCronJob {
101 if (class_exists($a_class)) {
102 if ($isCreationContext) {
103 $refl = new ReflectionClass($a_class);
104 $job = $refl->newInstanceWithoutConstructor();
105 } else {
106 $job = new $a_class();
107 }
108
109 if ($job instanceof ilCronJob && $job->getId() === $a_id) {
110 return $job;
111 }
112 }
113
114 return null;
115 }
116
123 public function getCronJobData($id = null, bool $withInactiveJobsIncluded = true): array
124 {
125 $jobData = [];
126
127 if ($id && !is_array($id)) {
128 $id = [$id];
129 }
130
131 $query = "SELECT * FROM cron_job";
132 $where = [];
133 if ($id) {
134 $where[] = $this->db->in('job_id', $id, false, 'text');
135 } else {
136 $where[] = 'class != ' . $this->db->quote(self::TYPE_PLUGINS, 'text');
137 }
138 if (!$withInactiveJobsIncluded) {
139 $where[] = 'job_status = ' . $this->db->quote(1, 'integer');
140 }
141 if ($where !== []) {
142 $query .= ' WHERE ' . implode(' AND ', $where);
143 }
144 // :TODO: discuss job execution order
145 $query .= ' ORDER BY job_id';
146
147 $res = $this->db->query($query);
148 while ($row = $this->db->fetchAssoc($res)) {
149 $jobData[] = $row;
150 }
151
152 return $jobData;
153 }
154
155 public function registerJob(
156 string $a_component,
157 string $a_id,
158 string $a_class,
159 ?string $a_path
160 ): void {
161 if (!$this->db->tableExists('cron_job')) {
162 return;
163 }
164
165 $job = $this->getJobInstance($a_id, $a_component, $a_class, true);
166 if ($job) {
167 $this->createDefaultEntry($job, $a_component, $a_class, $a_path);
168 }
169 }
170
171 public function unregisterJob(string $a_component, array $a_xml_job_ids): void
172 {
173 if (!$this->db->tableExists('cron_job')) {
174 return;
175 }
176
177 $jobs = [];
178 $query = 'SELECT job_id FROM cron_job WHERE component = ' . $this->db->quote($a_component, 'text');
179 $res = $this->db->query($query);
180 while ($row = $this->db->fetchAssoc($res)) {
181 $jobs[] = $row['job_id'];
182 }
183
184 if ($jobs !== []) {
185 if ($a_xml_job_ids !== []) {
186 foreach ($jobs as $job_id) {
187 if (!in_array($job_id, $a_xml_job_ids, true)) {
188 $this->db->manipulate(
189 'DELETE FROM cron_job' .
190 ' WHERE component = ' . $this->db->quote($a_component, 'text') .
191 ' AND job_id = ' . $this->db->quote($job_id, 'text')
192 );
193 }
194 }
195 } else {
196 $this->db->manipulate('DELETE FROM cron_job WHERE component = ' . $this->db->quote($a_component, 'text'));
197 }
198 }
199 }
200
201 public function createDefaultEntry(
202 ilCronJob $job,
203 string $component,
204 string $class,
205 ?string $path
206 ): void {
207 $query = "SELECT job_id, schedule_type, component, class, path FROM cron_job" .
208 " WHERE job_id = " . $this->db->quote($job->getId(), "text");
209 $res = $this->db->query($query);
210 $row = $this->db->fetchAssoc($res);
211 $job_id = $row['job_id'] ?? null;
212 $job_exists = ($job_id === $job->getId());
213 $schedule_type = $row["schedule_type"] ?? null;
214
215 if (
216 $job_exists && (
217 $row['component'] !== $component ||
218 $row['class'] !== $class ||
219 $row['path'] !== $path
220 )
221 ) {
222 $this->db->manipulateF(
223 'UPDATE cron_job SET component = %s, class = %s, path = %s WHERE job_id = %s',
224 ['text', 'text', 'text', 'text'],
225 [$component, $class, $path, $job->getId()]
226 );
227 }
228
229 // new job
230 if (!$job_exists) {
231 $query = 'INSERT INTO cron_job (job_id, component, class, path)' .
232 ' VALUES (' . $this->db->quote($job->getId(), 'text') . ', ' .
233 $this->db->quote($component, 'text') . ', ' .
234 $this->db->quote($class, 'text') . ', ' .
235 $this->db->quote($path, 'text') . ')';
236 $this->db->manipulate($query);
237
238 $this->logger->info('Cron XML - Job ' . $job->getId() . ' in class ' . $class . ' added.');
239
240 // only if flexible
241 $this->updateJobSchedule(
242 $job,
245 );
246
247 if ($job->hasAutoActivation()) {
248 $this->activateJob($job, new DateTimeImmutable('@' . time()));
249 $job->activationWasToggled($this->db, $this->setting, true);
250 } else {
251 // to overwrite dependent settings
252 $job->activationWasToggled($this->db, $this->setting, false);
253 }
254 } // existing job - but schedule is flexible now
255 elseif (!$schedule_type && $job->hasFlexibleSchedule()) {
256 $this->updateJobSchedule(
257 $job,
260 );
261 } // existing job - but schedule is static now
262 elseif ($schedule_type && !$job->hasFlexibleSchedule()) {
263 $this->updateJobSchedule($job, null, null);
264 }
265 }
266
271 public function getPluginJobs(bool $withOnlyActive = false): array
272 {
273 $res = [];
274 foreach ($this->componentRepository->getPlugins() as $pl) {
275 if (!$pl->isActive()) {
276 continue;
277 }
278
279 $plugin = $this->componentFactory->getPlugin($pl->getId());
280
281 if (!$plugin instanceof ilCronJobProvider) {
282 continue;
283 }
284
285 foreach ($plugin->getCronJobInstances() as $job) {
286 $jobs_data = $this->getCronJobData($job->getId());
287 $job_data = $jobs_data[0] ?? null;
288 if (!is_array($job_data) || $job_data === []) {
289 // as job is not "imported" from xml
290 $this->createDefaultEntry($job, $plugin->getPluginName(), self::TYPE_PLUGINS, '');
291 }
292
293 $jobs_data = $this->getCronJobData($job->getId());
294 $job_data = $jobs_data[0];
295
296 // #17941
297 if (!$withOnlyActive || (int) $job_data['job_status'] === 1) {
298 $res[$job->getId()] = [$job, $job_data];
299 }
300 }
301 }
302
303 return $res;
304 }
305
306 public function resetJob(ilCronJob $job): void
307 {
308 $this->db->manipulate('UPDATE cron_job' .
309 ' SET running_ts = ' . $this->db->quote(0, 'integer') .
310 ' , alive_ts = ' . $this->db->quote(0, 'integer') .
311 ' , job_result_ts = ' . $this->db->quote(0, 'integer') .
312 ' WHERE job_id = ' . $this->db->quote($job->getId(), 'text'));
313 }
314
315 public function updateJobResult(
316 ilCronJob $job,
317 DateTimeImmutable $when,
318 ilObjUser $actor,
319 ilCronJobResult $result,
320 bool $wasManualExecution = false
321 ): void {
322 $user_id = $wasManualExecution ? $actor->getId() : 0;
323
324 $query = 'UPDATE cron_job SET ' .
325 ' job_result_status = ' . $this->db->quote($result->getStatus(), 'integer') .
326 ' , job_result_user_id = ' . $this->db->quote($user_id, 'integer') .
327 ' , job_result_code = ' . $this->db->quote($result->getCode(), 'text') .
328 ' , job_result_message = ' . $this->db->quote($result->getMessage(), 'text') .
329 ' , job_result_type = ' . $this->db->quote((int) $wasManualExecution, 'integer') .
330 ' , job_result_ts = ' . $this->db->quote($when->getTimestamp(), 'integer') .
331 ' , job_result_dur = ' . $this->db->quote($result->getDuration() * 1000, 'integer') .
332 ' WHERE job_id = ' . $this->db->quote($job->getId(), 'text');
333 $this->db->manipulate($query);
334 }
335
336 public function updateRunInformation(string $jobId, int $runningTimestamp, int $aliveTimestamp): void
337 {
338 $this->db->manipulate('UPDATE cron_job SET' .
339 ' running_ts = ' . $this->db->quote($runningTimestamp, 'integer') .
340 ' , alive_ts = ' . $this->db->quote($aliveTimestamp, 'integer') .
341 ' WHERE job_id = ' . $this->db->quote($jobId, 'text'));
342 }
343
344 public function updateJobSchedule(ilCronJob $job, ?int $scheduleType, ?int $scheduleValue): void
345 {
346 if (
347 $scheduleType === null ||
348 ($job->hasFlexibleSchedule() && in_array($scheduleType, $job->getValidScheduleTypes(), true))
349 ) {
350 $query = 'UPDATE cron_job SET ' .
351 ' schedule_type = ' . $this->db->quote($scheduleType, 'integer') .
352 ' , schedule_value = ' . $this->db->quote($scheduleValue, 'integer') .
353 ' WHERE job_id = ' . $this->db->quote($job->getId(), 'text');
354 $this->db->manipulate($query);
355 }
356 }
357
358 public function activateJob(
359 ilCronJob $job,
360 DateTimeImmutable $when,
361 ?ilObjUser $actor = null,
362 bool $wasManuallyExecuted = false
363 ): void {
364 $usrId = 0;
365 if ($wasManuallyExecuted && $actor instanceof ilObjUser) {
366 $usrId = $actor->getId();
367 }
368
369 $query = 'UPDATE cron_job SET ' .
370 ' job_status = ' . $this->db->quote(1, 'integer') .
371 ' , job_status_user_id = ' . $this->db->quote($usrId, 'integer') .
372 ' , job_status_type = ' . $this->db->quote($wasManuallyExecuted, 'integer') .
373 ' , job_status_ts = ' . $this->db->quote($when->getTimestamp(), 'integer') .
374 ' WHERE job_id = ' . $this->db->quote($job->getId(), 'text');
375 $this->db->manipulate($query);
376 }
377
378 public function deactivateJob(
379 ilCronJob $job,
380 DateTimeImmutable $when,
381 ilObjUser $actor,
382 bool $wasManuallyExecuted = false
383 ): void {
384 $usrId = $wasManuallyExecuted ? $actor->getId() : 0;
385
386 $query = 'UPDATE cron_job SET ' .
387 ' job_status = ' . $this->db->quote(0, 'integer') .
388 ' , job_result_status = ' . $this->db->quote(null, 'text') .
389 ' , job_result_message = ' . $this->db->quote(null, 'text') .
390 ' , job_result_type = ' . $this->db->quote(null, 'text') .
391 ' , job_result_code = ' . $this->db->quote(null, 'text') .
392 ' , job_status_user_id = ' . $this->db->quote($usrId, 'integer') .
393 ' , job_status_type = ' . $this->db->quote($wasManuallyExecuted, 'integer') .
394 ' , job_status_ts = ' . $this->db->quote($when->getTimestamp(), 'integer') .
395 ' WHERE job_id = ' . $this->db->quote($job->getId(), 'text');
396 $this->db->manipulate($query);
397 }
398
399 public function findAll(): ilCronJobCollection
400 {
401 $collection = new ilCronJobEntities();
402
403 foreach ($this->getCronJobData() as $item) {
404 $job = $this->getJobInstance(
405 $item['job_id'],
406 $item['component'],
407 $item['class']
408 );
409 if ($job) {
410 $collection->add(new ilCronJobEntity($job, $item));
411 }
412 }
413
414 foreach ($this->getPluginJobs() as $item) {
415 $collection->add(new ilCronJobEntity($item[0], $item[1], true));
416 }
417
418 return $collection;
419 }
420}
$id
plugin.php for ilComponentBuildPluginInfoObjectiveTest::testAddPlugins
Definition: plugin.php:23
unregisterJob(string $a_component, array $a_xml_job_ids)
getJobInstance(string $a_id, string $a_component, string $a_class, bool $isCreationContext=false)
getCronJobData($id=null, bool $withInactiveJobsIncluded=true)
Get cron job configuration/execution data.
activateJob(ilCronJob $job, DateTimeImmutable $when, ?ilObjUser $actor=null, bool $wasManuallyExecuted=false)
updateJobResult(ilCronJob $job, DateTimeImmutable $when, ilObjUser $actor, ilCronJobResult $result, bool $wasManualExecution=false)
registerJob(string $a_component, string $a_id, string $a_class, ?string $a_path)
updateRunInformation(string $jobId, int $runningTimestamp, int $aliveTimestamp)
updateJobSchedule(ilCronJob $job, ?int $scheduleType, ?int $scheduleValue)
deactivateJob(ilCronJob $job, DateTimeImmutable $when, ilObjUser $actor, bool $wasManuallyExecuted=false)
__construct(ilDBInterface $db, ilSetting $setting, ilLogger $logger, ilComponentRepository $componentRepository, ilComponentFactory $componentFactory)
createDefaultEntry(ilCronJob $job, string $component, string $class, ?string $path)
ilComponentRepository $componentRepository
getPluginJobs(bool $withOnlyActive=false)
getDefaultScheduleType()
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...
getDefaultScheduleValue()
hasAutoActivation()
Is to be activated on "installation", does only work for ILIAS core cron jobs.
hasFlexibleSchedule()
getValidScheduleTypes()
Returns a collection of all valid schedule types for a specific job.
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...
if(!file_exists(getcwd() . '/ilias.ini.php'))
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
Definition: confirmReg.php:20
Readable part of repository interface to ilComponentDataDB.
Interface ilDBInterface.
$path
Definition: ltiservices.php:32
$res
Definition: ltiservices.php:69
if($clientAssertionType !='urn:ietf:params:oauth:client-assertion-type:jwt-bearer'|| $grantType !='client_credentials') $parts
Definition: ltitoken.php:64
$query