ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
class.ilCronManager.php
Go to the documentation of this file.
1<?php
2
3/* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
4
12{
16 public static function runActiveJobs()
17 {
18 global $ilLog, $ilSetting;
19
20 // separate log for cron
21 // $this->log->setFilename($_COOKIE["ilClientId"]."_cron.txt");
22
23 $ilLog->write("CRON - batch start");
24
25 $ts = time();
26 $ilSetting->set("last_cronjob_start_ts", $ts);
27
28 $useRelativeDates = ilDatePresentation::useRelativeDates();
30 $ilLog->info(sprintf('Set last datetime to: %s', ilDatePresentation::formatDate(new ilDateTime($ts, IL_CAL_UNIX))));
31 $ilLog->info(sprintf(
32 'Verification of last run datetime (read from database): %s',
34 new ilDateTime(ilSetting::_lookupValue('common', 'last_cronjob_start_ts'), IL_CAL_UNIX)
35 )
36 ));
37 ilDatePresentation::setUseRelativeDates((bool) $useRelativeDates);
38
39 // ilLink::_getStaticLink() should work in crons
40 if (!defined("ILIAS_HTTP_PATH")) {
41 define("ILIAS_HTTP_PATH", ilUtil::_getHttpPath());
42 }
43
44 // system
45 foreach (self::getCronJobData(null, false) as $row) {
46 $job = self::getJobInstanceById($row["job_id"]);
47 if ($job) {
48 // #18411 - we are NOT using the initial job data as it might be outdated at this point
49 self::runJob($job);
50 }
51 }
52
53 // plugins
54 foreach (self::getPluginJobs(true) as $item) {
55 self::runJob($item[0], $item[1]);
56 }
57
58 $ilLog->write("CRON - batch end");
59 }
60
67 public static function runJobManual($a_job_id)
68 {
69 global $ilLog;
70
71 $result = false;
72
73 $ilLog->write("CRON - manual start (" . $a_job_id . ")");
74
75 $job = self::getJobInstanceById($a_job_id);
76 if ($job) {
77 if ($job->isManuallyExecutable()) {
78 $result = self::runJob($job, null, true);
79 } else {
80 $ilLog->write("CRON - job " . $a_job_id . " is not intended to be executed manually");
81 }
82 } else {
83 $ilLog->write("CRON - job " . $a_job_id . " seems invalid or is inactive");
84 }
85
86 $ilLog->write("CRON - manual end (" . $a_job_id . ")");
87
88 return $result;
89 }
90
99 protected static function runJob(ilCronJob $a_job, array $a_job_data = null, $a_manual = false)
100 {
101 global $ilLog, $ilDB;
102
103 $did_run = false;
104
105 include_once "Services/Cron/classes/class.ilCronJobResult.php";
106
107 if ($a_job_data === null) {
108 // aquire "fresh" job (status) data
109 $a_job_data = array_pop(self::getCronJobData($a_job->getId()));
110 }
111
112 // already running?
113 if ($a_job_data["alive_ts"]) {
114 $ilLog->write("CRON - job " . $a_job_data["job_id"] . " still running");
115
116 $cut = 60*60*3; // 3h
117
118 // is running (and has not pinged) for 3 hours straight, we assume it crashed
119 if (time()-$a_job_data["alive_ts"] > $cut) {
120 $ilDB->manipulate("UPDATE cron_job SET" .
121 " running_ts = " . $ilDB->quote(0, "integer") .
122 " , alive_ts = " . $ilDB->quote(0, "integer") .
123 " WHERE job_id = " . $ilDB->quote($a_job_data["job_id"], "text"));
124
125 self::deactivateJob($a_job); // #13082
126
127 $result = new ilCronJobResult();
130 $result->setMessage("Cron job deactivated because it has been inactive for 3 hours");
131
132 if (!$a_manual) {
134 }
135
136 self::updateJobResult($a_job, $result, $a_manual);
137
138 $ilLog->write("CRON - job " . $a_job_data["job_id"] . " deactivated (assumed crash)");
139 }
140 }
141 // initiate run?
142 elseif ($a_job->isActive(
143 $a_job_data["job_result_ts"],
144 $a_job_data["schedule_type"],
145 $a_job_data["schedule_value"],
146 $a_manual
147 )) {
148 $ilLog->write("CRON - job " . $a_job_data["job_id"] . " started");
149
150 $ilDB->manipulate("UPDATE cron_job SET" .
151 " running_ts = " . $ilDB->quote(time(), "integer") .
152 " , alive_ts = " . $ilDB->quote(time(), "integer") .
153 " WHERE job_id = " . $ilDB->quote($a_job_data["job_id"], "text"));
154
155 $ts_in = self::getMicrotime();
156 try {
157 $result = $a_job->run();
158 } catch (\Exception $e) {
159 $result = new \ilCronJobResult();
161 $result->setMessage(sprintf("Exception: %s", $e->getMessage()));
162
163 $ilLog->error($e->getMessage());
164 $ilLog->error($e->getTraceAsString());
165 } catch (\Throwable $e) { // Could be appended to the catch block with a | in PHP 7.1
166 $result = new \ilCronJobResult();
168 $result->setMessage(sprintf("Exception: %s", $e->getMessage()));
169
170 $ilLog->error($e->getMessage());
171 $ilLog->error($e->getTraceAsString());
172 }
173 $ts_dur = self::getMicrotime()-$ts_in;
174
175 // no proper result
176 if (!$result instanceof ilCronJobResult) {
177 $result = new ilCronJobResult();
180 $result->setMessage("Cron job did not return a proper result");
181
182 if (!$a_manual) {
184 }
185
186 $ilLog->write("CRON - job " . $a_job_data["job_id"] . " no result");
187 }
188 // no valid configuration, job won't work
190 self::deactivateJob($a_job);
191
192 if (!$a_manual) {
194 }
195
196 $ilLog->write("CRON - job " . $a_job_data["job_id"] . " invalid configuration");
197 }
198 // success!
199 else {
200 $did_run = true;
201 }
202
203 $result->setDuration($ts_dur);
204
205 self::updateJobResult($a_job, $result, $a_manual);
206
207 $ilDB->manipulate("UPDATE cron_job SET" .
208 " running_ts = " . $ilDB->quote(0, "integer") .
209 " , alive_ts = " . $ilDB->quote(0, "integer") .
210 " WHERE job_id = " . $ilDB->quote($a_job_data["job_id"], "text"));
211
212 $ilLog->write("CRON - job " . $a_job_data["job_id"] . " finished");
213 } else {
214 $ilLog->write("CRON - job " . $a_job_data["job_id"] . " returned status inactive");
215 }
216
217 return $did_run;
218 }
219
226 public static function getJobInstanceById($a_job_id)
227 {
228 global $ilLog, $ilPluginAdmin;
229
230 // plugin
231 if (substr($a_job_id, 0, 4) == "pl__") {
232 $parts = explode("__", $a_job_id);
233 $pl_name = $parts[1];
234 $job_id = $parts[2];
235 if ($ilPluginAdmin->isActive(IL_COMP_SERVICE, "Cron", "crnhk", $pl_name)) {
236 $plugin_obj = $ilPluginAdmin->getPluginObject(
238 "Cron",
239 "crnhk",
240 $pl_name
241 );
242 $job = $plugin_obj->getCronJobInstance($job_id);
243 if ($job instanceof ilCronJob) {
244 // should never happen but who knows...
245 if (!sizeof(ilCronManager::getCronJobData($job_id))) {
246 // as job is not "imported" from xml
248 }
249 return $job;
250 }
251 }
252
253 return null;
254 }
255 // system
256 else {
257 $job_data = array_pop(self::getCronJobData($a_job_id));
258 if ($job_data["job_id"] == $a_job_id) {
260 $job_data["job_id"],
261 $job_data["component"],
262 $job_data["class"],
263 $job_data["path"]
264 );
265 }
266 }
267
268 $ilLog->write("CRON - job " . $a_job_id . " seems invalid or is inactive");
269 }
270
279 public static function getJobInstance($a_id, $a_component, $a_class, $a_path = null)
280 {
281 global $ilLog;
282
283 if (!$a_path) {
284 $a_path = $a_component . "/classes/";
285 }
286 $class_file = $a_path . "class." . $a_class . ".php";
287 if (file_exists($class_file)) {
288 include_once $class_file;
289 if (class_exists($a_class)) {
290 $job = new $a_class();
291 if ($job instanceof ilCronJob) {
292 if ($job->getId() == $a_id) {
293 return $job;
294 } else {
295 $mess .= " - job id mismatch";
296 }
297 } else {
298 $mess .= " - does not extend ilCronJob";
299 }
300 } else {
301 $mess = "- class not found in file";
302 }
303 } else {
304 $mess = " - class file not found";
305 }
306
307 $ilLog->write("Cron XML - Job " . $a_id . " in class " . $a_class . " (" .
308 $class_file . ") is invalid." . $mess);
309 }
310
317 protected static function sendNotification(ilCronJob $a_job, $a_message)
318 {
319 // :TODO:
320 }
321
322 public static function createDefaultEntry(ilCronJob $a_job, $a_component, $a_class, $a_path)
323 {
324 global $ilDB, $ilLog, $ilSetting;
325
326 // already exists?
327 $sql = "SELECT job_id, schedule_type FROM cron_job" .
328 " WHERE component = " . $ilDB->quote($a_component, "text") .
329 " AND job_id = " . $ilDB->quote($a_job->getId(), "text");
330 $set = $ilDB->query($sql);
331 $row = $ilDB->fetchAssoc($set);
332 $job_exists = ($row["job_id"] == $a_job->getId());
333 $schedule_type = $row["schedule_type"];
334
335 // new job
336 if (!$job_exists) {
337 $sql = "INSERT INTO cron_job (job_id, component, class, path)" .
338 " VALUES (" . $ilDB->quote($a_job->getId(), "text") . ", " .
339 $ilDB->quote($a_component, "text") . ", " .
340 $ilDB->quote($a_class, "text") . ", " .
341 $ilDB->quote($a_path, "text") . ")";
342 $ilDB->manipulate($sql);
343
344 $ilLog->write("Cron XML - Job " . $a_job->getId() . " in class " . $a_class .
345 " added.");
346
347 // only if flexible
349 $a_job,
350 $a_job->getDefaultScheduleType(),
352 );
353
354 // #12221
355 if (!is_object($ilSetting)) {
356 include_once "Services/Administration/classes/class.ilSetting.php";
357 $ilSetting = new ilSetting();
358 }
359
360 if ($a_job->hasAutoActivation()) {
361 self::activateJob($a_job);
362 } else {
363 // to overwrite dependent settings
364 $a_job->activationWasToggled(false);
365 }
366 }
367 // existing job - but schedule is flexible now
368 elseif ($a_job->hasFlexibleSchedule() && !$schedule_type) {
370 $a_job,
371 $a_job->getDefaultScheduleType(),
373 );
374 }
375 // existing job - but schedule is static now
376 elseif (!$a_job->hasFlexibleSchedule() && $schedule_type) {
377 self::updateJobSchedule($a_job, null, null);
378 }
379 }
380
389 public static function updateFromXML($a_component, $a_id, $a_class, $a_path = null)
390 {
391 global $ilDB;
392
393 if (!$ilDB->tableExists("cron_job")) {
394 return;
395 }
396
397 // only if job seems valid
398 $job = self::getJobInstance($a_id, $a_component, $a_class, $a_path);
399 if ($job) {
400 self::createDefaultEntry($job, $a_component, $a_class, $a_path);
401 }
402 }
403
410 public static function clearFromXML($a_component, array $a_xml_job_ids)
411 {
412 global $ilDB, $ilLog;
413
414 if (!$ilDB->tableExists("cron_job")) {
415 return;
416 }
417
418 // gather existing jobs
419 $all_jobs = array();
420 $sql = "SELECT job_id FROM cron_job" .
421 " WHERE component = " . $ilDB->quote($a_component, "text");
422 $set = $ilDB->query($sql);
423 while ($row = $ilDB->fetchAssoc($set)) {
424 $all_jobs[] = $row["job_id"];
425 }
426
427 if (sizeof($all_jobs)) {
428 if (sizeof($a_xml_job_ids)) {
429 // delete obsolete job data
430 foreach ($all_jobs as $job_id) {
431 if (!in_array($job_id, $a_xml_job_ids)) {
432 $ilDB->manipulate("DELETE FROM cron_job" .
433 " WHERE component = " . $ilDB->quote($a_component, "text") .
434 " AND job_id = " . $ilDB->quote($job_id, "text"));
435
436 $ilLog->write("Cron XML - Job " . $job_id . " in class " . $a_component .
437 " deleted.");
438 }
439 }
440 } else {
441 $ilDB->manipulate("DELETE FROM cron_job" .
442 " WHERE component = " . $ilDB->quote($a_component, "text"));
443
444 $ilLog->write("Cron XML - All jobs deleted for " . $a_component . " as component is inactive.");
445 }
446 }
447 }
448
449 public static function getPluginJobs($a_only_active = false)
450 {
451 global $ilPluginAdmin;
452
453 $res = array();
454
455 foreach ($ilPluginAdmin->getActivePluginsForSlot(IL_COMP_SERVICE, "Cron", "crnhk") as $pl_name) {
456 $plugin_obj = $ilPluginAdmin->getPluginObject(IL_COMP_SERVICE, "Cron", "crnhk", $pl_name);
457
458 foreach ((array) $plugin_obj->getCronJobInstances() as $job) {
459 $item = array_pop(ilCronManager::getCronJobData($job->getId()));
460 if (!is_array($item) || 0 === count($item)) {
461 // as job is not "imported" from xml
463 }
464
465 $item = array_pop(ilCronManager::getCronJobData($job->getId()));
466
467 // #17941
468 if (!$a_only_active ||
469 $item["job_status"] == 1) {
470 $res[$job->getId()] = array($job, $item);
471 }
472 }
473 }
474
475 return $res;
476 }
477
485 public static function getCronJobData($a_id = null, $a_include_inactive = true)
486 {
487 global $ilDB;
488
489 $res = array();
490
491 if ($a_id && !is_array($a_id)) {
492 $a_id = array($a_id);
493 }
494
495 $sql = "SELECT * FROM cron_job";
496
497 $where = array();
498 if ($a_id) {
499 $where[] = $ilDB->in("job_id", $a_id, "", "text");
500 } else {
501 $where[] = "class <> " . $ilDB->quote(IL_COMP_PLUGIN, "text");
502 }
503 if (!$a_include_inactive) {
504 $where[] = "job_status = " . $ilDB->quote(1, "integer");
505 }
506 if (sizeof($where)) {
507 $sql .= " WHERE " . implode(" AND ", $where);
508 }
509
510 // :TODO: discuss job execution order
511 $sql .= " ORDER BY job_id";
512
513 $set = $ilDB->query($sql);
514 while ($row = $ilDB->fetchAssoc($set)) {
515 $res[] = $row;
516 }
517
518 return $res;
519 }
520
526 public static function resetJob(ilCronJob $a_job)
527 {
528 global $ilDB;
529
530 include_once "Services/Cron/classes/class.ilCronJobResult.php";
531 $result = new ilCronJobResult();
534 $result->setMessage("Cron job re-activated by admin");
535 self::updateJobResult($a_job, $result, true);
536
537 $ilDB->manipulate("UPDATE cron_job" .
538 " SET running_ts = " . $ilDB->quote(0, "integer") .
539 " , alive_ts = " . $ilDB->quote(0, "integer") .
540 " , job_result_ts = " . $ilDB->quote(0, "integer") .
541 " WHERE job_id = " . $ilDB->quote($a_job->getId(), "text"));
542
543 self::activateJob($a_job, true);
544 }
545
552 public static function activateJob(ilCronJob $a_job, $a_manual = false)
553 {
554 global $ilDB, $ilUser;
555
556 $user_id = $a_manual ? $ilUser->getId() : 0;
557
558 $sql = "UPDATE cron_job SET " .
559 " job_status = " . $ilDB->quote(1, "integer") .
560 " , job_status_user_id = " . $ilDB->quote($user_id, "integer") .
561 " , job_status_type = " . $ilDB->quote($a_manual, "integer") .
562 " , job_status_ts = " . $ilDB->quote(time(), "integer") .
563 " WHERE job_id = " . $ilDB->quote($a_job->getId(), "text");
564 $ilDB->manipulate($sql);
565
566 $a_job->activationWasToggled(true);
567 }
568
575 public static function deactivateJob(ilCronJob $a_job, $a_manual = false)
576 {
577 global $ilDB, $ilUser;
578
579 $user_id = $a_manual ? $ilUser->getId() : 0;
580
581 $sql = "UPDATE cron_job SET " .
582 " job_status = " . $ilDB->quote(0, "integer") .
583 " , job_status_user_id = " . $ilDB->quote($user_id, "integer") .
584 " , job_status_type = " . $ilDB->quote($a_manual, "integer") .
585 " , job_status_ts = " . $ilDB->quote(time(), "integer") .
586 " WHERE job_id = " . $ilDB->quote($a_job->getId(), "text");
587 $ilDB->manipulate($sql);
588
589 $a_job->activationWasToggled(false);
590 }
591
598 public static function isJobActive($a_job_id)
599 {
600 $job = self::getCronJobData($a_job_id);
601 if ((bool) $job[0]["job_status"]) {
602 return true;
603 }
604 return false;
605 }
606
613 public static function isJobInactive($a_job_id)
614 {
615 $job = self::getCronJobData($a_job_id);
616 if (!(bool) $job[0]["job_status"]) {
617 return true;
618 }
619 return false;
620 }
621
629 protected static function updateJobResult(ilCronJob $a_job, ilCronJobResult $a_result, $a_manual = false)
630 {
631 global $ilDB, $ilUser;
632
633 $user_id = $a_manual ? $ilUser->getId() : 0;
634
635 $sql = "UPDATE cron_job SET " .
636 " job_result_status = " . $ilDB->quote($a_result->getStatus(), "integer") .
637 " , job_result_user_id = " . $ilDB->quote($user_id, "integer") .
638 " , job_result_code = " . $ilDB->quote($a_result->getCode(), "text") .
639 " , job_result_message = " . $ilDB->quote($a_result->getMessage(), "text") .
640 " , job_result_type = " . $ilDB->quote($a_manual, "integer") .
641 " , job_result_ts = " . $ilDB->quote(time(), "integer") .
642 " , job_result_dur = " . $ilDB->quote($a_result->getDuration()*1000, "integer") .
643 " WHERE job_id = " . $ilDB->quote($a_job->getId(), "text");
644 $ilDB->manipulate($sql);
645 }
646
654 public static function updateJobSchedule(ilCronJob $a_job, $a_schedule_type, $a_schedule_value)
655 {
656 global $ilDB;
657
658 if ($a_schedule_type === null ||
659 ($a_job->hasFlexibleSchedule() &&
660 in_array($a_schedule_type, $a_job->getValidScheduleTypes()))) {
661 $sql = "UPDATE cron_job SET " .
662 " schedule_type = " . $ilDB->quote($a_schedule_type, "integer") .
663 " , schedule_value = " . $ilDB->quote($a_schedule_value, "integer") .
664 " WHERE job_id = " . $ilDB->quote($a_job->getId(), "text");
665 $ilDB->manipulate($sql);
666 }
667 }
668
674 protected static function getMicrotime()
675 {
676 list($usec, $sec) = explode(" ", microtime());
677 return ((float) $usec + (float) $sec);
678 }
679
685 public static function ping($a_job_id)
686 {
687 global $ilDB;
688
689 $ilDB->manipulate("UPDATE cron_job SET " .
690 " alive_ts = " . $ilDB->quote(time(), "integer") .
691 " WHERE job_id = " . $ilDB->quote($a_job_id, "text"));
692 }
693}
sprintf('%.4f', $callTime)
$result
An exception for terminatinating execution or to throw for unit testing.
const IL_COMP_SERVICE
const IL_COMP_PLUGIN
const IL_CAL_UNIX
Cron job result data container.
Cron job application base class.
getDefaultScheduleType()
Get schedule type.
activationWasToggled($a_currently_active)
Cron job status was changed.
run()
Run job.
getDefaultScheduleValue()
Get schedule value.
getId()
Get id.
isActive($a_ts_last_run, $a_schedule_type, $a_schedule_value, $a_manual=false)
Is job currently active?
hasAutoActivation()
Is to be activated on "installation".
hasFlexibleSchedule()
Can the schedule be configured?
getValidScheduleTypes()
Get all available schedule types.
Cron management.
static getCronJobData($a_id=null, $a_include_inactive=true)
Get cron job configuration/execution data.
static sendNotification(ilCronJob $a_job, $a_message)
Send notification to admin about job event(s)
static updateFromXML($a_component, $a_id, $a_class, $a_path=null)
Process data from module.xml/service.xml.
static createDefaultEntry(ilCronJob $a_job, $a_component, $a_class, $a_path)
static isJobInactive($a_job_id)
Check if given job is currently inactive.
static getJobInstanceById($a_job_id)
Get job instance (by job id)
static runJobManual($a_job_id)
Run single job manually.
static activateJob(ilCronJob $a_job, $a_manual=false)
Activate cron job.
static getJobInstance($a_id, $a_component, $a_class, $a_path=null)
Get job instance (by job data)
static isJobActive($a_job_id)
Check if given job is currently active.
static deactivateJob(ilCronJob $a_job, $a_manual=false)
Deactivate cron job.
static updateJobResult(ilCronJob $a_job, ilCronJobResult $a_result, $a_manual=false)
Save job result.
static clearFromXML($a_component, array $a_xml_job_ids)
Clear job data.
static resetJob(ilCronJob $a_job)
Reset job.
static ping($a_job_id)
Keep cron job alive.
static updateJobSchedule(ilCronJob $a_job, $a_schedule_type, $a_schedule_value)
Update job schedule.
static runJob(ilCronJob $a_job, array $a_job_data=null, $a_manual=false)
Run single cron job (internal)
static runActiveJobs()
Run all active jobs.
static getMicrotime()
Get current microtime.
static getPluginJobs($a_only_active=false)
static formatDate(ilDateTime $date, $a_skip_day=false, $a_include_wd=false)
Format a date @access public.
static setUseRelativeDates($a_status)
set use relative dates
static useRelativeDates()
check if relative dates are used
@classDescription Date and time handling
ILIAS Setting Class.
static _lookupValue($a_module, $a_keyword)
static _getHttpPath()
defined( 'APPLICATION_ENV')||define( 'APPLICATION_ENV'
Definition: bootstrap.php:27
global $ilSetting
Definition: privfeed.php:17
foreach($_POST as $key=> $value) $res
global $ilDB
$ilUser
Definition: imgupload.php:18