8 use Elgg\Traits\Loggable;
9 use Elgg\Traits\TimeUsing;
23 protected const LOG_FILES_TO_KEEP = 5;
25 protected array $default_intervals = [
26 'minute' =>
'* * * * *',
27 'fiveminute' =>
'*/5 * * * *',
28 'fifteenmin' =>
'*/15 * * * *',
29 'halfhour' =>
'*/30 * * * *',
30 'hourly' =>
'0 * * * *',
31 'daily' =>
'0 0 * * *',
32 'weekly' =>
'0 0 * * 0',
33 'monthly' =>
'0 0 1 * *',
34 'yearly' =>
'0 0 1 1 *',
55 public function run(?array $intervals =
null,
bool $force =
false): array {
56 if (!isset($intervals)) {
57 $intervals = array_keys($this->default_intervals);
60 $allowed_intervals = $this->getConfiguredIntervals();
62 $scheduler =
new Scheduler();
63 $time = $this->getCurrentTime();
64 $immutable = \DateTimeImmutable::createFromInterface(
$time);
66 foreach ($intervals as $interval) {
67 if (!array_key_exists($interval, $allowed_intervals)) {
68 throw new CronException(
"{$interval} is not a recognized cron interval. Please use one of the following: " . implode(
', ', array_keys($allowed_intervals)));
71 $cron_interval = $force ? $allowed_intervals[
'minute'] : $allowed_intervals[$interval];
72 $filename = $this->getLogFilename($interval, $immutable);
74 $cron_logger = \Elgg\Logger\Cron::factory([
75 'interval' => $interval,
80 ->call(
function () use ($interval, $immutable, $cron_logger,
$filename) {
81 return $this->execute($interval, $cron_logger,
$filename, $immutable);
84 ->before(
function () use ($interval, $immutable, $cron_logger) {
85 $this->before($interval, $cron_logger, $immutable);
87 ->then(
function (
$output) use ($interval, $cron_logger) {
88 $this->after(
$output, $interval, $cron_logger);
92 return $scheduler->run(
$time);
106 $this->events->triggerBefore(
'cron', $interval,
$time);
107 }
catch (\Throwable $t) {
108 $this->getLogger()->error($t);
112 set_time_limit((
int) ini_get(
'max_execution_time'));
114 $now =
new DateTime();
116 $cron_logger->notice($this->translator->translate(
'admin:cron:started', [$interval,
$time->format(DATE_RFC2822)]));
117 $cron_logger->notice($this->translator->translate(
'admin:cron:started:actual', [$interval, $now->format(DATE_RFC2822)]));
132 $begin_callback = function (array
$params) use ($cron_logger) {
135 $cron_logger->notice(
"Starting {$readable_callable}");
138 $end_callback =
function (array
$params) use ($cron_logger) {
141 $cron_logger->notice(
"Finished {$readable_callable}");
145 $this->events->triggerResults(
'cron', $interval, [
146 'time' =>
$time->getTimestamp(),
148 'logger' => $cron_logger,
150 EventsService::OPTION_BEGIN_CALLBACK => $begin_callback,
151 EventsService::OPTION_END_CALLBACK => $end_callback,
153 }
catch (\Throwable $t) {
157 $now =
new DateTime();
159 $complete = $this->translator->translate(
'admin:cron:complete', [$interval, $now->format(DATE_RFC2822)]);
160 $cron_logger->notice($complete);
179 $this->getLogger()->info(
$output);
182 $this->events->triggerAfter(
'cron', $interval,
new \
DateTime());
183 }
catch (\Throwable $t) {
184 $this->getLogger()->error($t);
187 $cron_logger->close();
188 $this->rotateLogs($interval);
189 $this->logCompletion($interval);
202 public function getLogs(
string $interval,
bool $filenames_only =
false): array {
205 $fh->setFilename(
"cron/{$interval}/dummy.log");
207 $dir = pathinfo($fh->getFilenameOnFilestore(), PATHINFO_DIRNAME);
208 if (!is_dir($dir) || !is_readable($dir)) {
212 $dh = new \DirectoryIterator($dir);
215 foreach ($dh as $file) {
216 if ($file->isDot() || !$file->isFile() || $file->getExtension() !==
'log') {
220 if ($filenames_only) {
221 $files[] = $file->getFilename();
223 $files[$file->getFilename()] = file_get_contents($file->getPathname());
227 if ($filenames_only) {
230 uksort($files,
'strnatcasecmp');
233 return array_reverse($files);
246 $fh->setFilename(
"cron/{$interval}.complete");
248 if (!$fh->exists()) {
252 $date = $fh->grabFile();
259 return Values::normalizeTime($date);
260 }
catch (\
Elgg\Exceptions\ExceptionInterface $e) {
276 $result = $this->events->triggerResults(
'cron:intervals',
'system', [], $this->default_intervals);
278 $this->getLogger()->warning(
"The event 'cron:intervals', 'system' should return an array, " . gettype(
$result) .
' given');
280 $result = $this->default_intervals;
299 $date =
$time->format(\DateTimeInterface::ATOM);
300 $date = str_replace(
'+',
'p', $date);
301 $date = preg_replace(
'/[^a-zA-Z0-9_-]+/',
'-', $date);
303 $fh = new \ElggFile();
305 $fh->setFilename(
"cron/{$interval}/{$date}.log");
307 return $fh->getFilenameOnFilestore();
318 $files = $this->getLogs($interval, true);
319 if (count($files) <= self::LOG_FILES_TO_KEEP) {
323 $fh = new \ElggFile();
326 while (count($files) > self::LOG_FILES_TO_KEEP) {
329 $fh->setFilename(
"cron/{$interval}/{$filename}");
344 $fh->setFilename(
"cron/{$interval}.complete");
347 if ($fh->open(
'write') ===
false) {
351 $this->getLogger()->warning($e);
355 $now =
new DateTime();
356 $fh->write($now->format(\DateTimeInterface::ATOM));
getLogger()
Returns logger.
$params
Saves global plugin settings.
if(! $annotation instanceof ElggAnnotation) $time
before(string $interval, \Elgg\Logger\Cron $cron_logger, \DateTimeImmutable $time)
Execute commands before cron interval is run.
execute(string $interval, \Elgg\Logger\Cron $cron_logger, string $filename, \DateTimeImmutable $time)
Execute handlers attached to a specific cron interval.
getLogFilename(string $interval, \DateTimeInterface $time)
Get a filename to log in.
logCompletion(string $interval)
Log the completion time of a cron interval.
getLogs(string $interval, bool $filenames_only=false)
Get the log files for a given cron interval.
getLastCompletion(string $interval)
Get the time of the last completion of a cron interval.
run(?array $intervals=null, bool $force=false)
Executes handlers for periods that have elapsed since last cron.
after(string $output, string $interval, \Elgg\Logger\Cron $cron_logger)
Printers handler result.
getConfiguredIntervals(bool $only_names=false)
Get the cron interval configuration.
__construct(protected EventsService $events, protected Translator $translator)
Constructor.
rotateLogs(string $interval)
Rotate the log files.
A generic parent class for cron exceptions.
Extension of the DateTime class to support formatting a date using the locale.
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
elgg_get_site_entity()
Get the current site entity.
Generic interface which allows catching of all exceptions thrown in Elgg.
if(parse_url(elgg_get_site_url(), PHP_URL_PATH) !=='/') if(file_exists(elgg_get_root_path() . 'robots.txt'))
Set robots.txt.