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();
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) {
112 set_time_limit((
int) ini_get(
'max_execution_time'));
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) {
159 $complete = $this->translator->translate(
'admin:cron:complete', [$interval, $now->format(DATE_RFC2822)]);
160 $cron_logger->notice($complete);
162 if (file_exists($filename) && is_readable($filename)) {
163 return file_get_contents($filename);
182 $this->events->triggerAfter(
'cron', $interval,
new \
DateTime());
183 }
catch (\Throwable $t) {
187 $cron_logger->close();
188 $this->rotateLogs($interval);
189 $this->logCompletion($interval);
202 public function getLogs(
string $interval,
bool $filenames_only =
false): array {
203 $fh = new \ElggFile();
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);
244 $fh = new \ElggFile();
246 $fh->setFilename(
"cron/{$interval}.complete");
248 if (!$fh->exists()) {
252 $date = $fh->grabFile();
259 return Values::normalizeTime($date);
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);
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}");
342 $fh = new \ElggFile();
344 $fh->setFilename(
"cron/{$interval}.complete");
347 if ($fh->open(
'write') ===
false) {
356 $fh->write($now->format(\DateTimeInterface::ATOM));
$params
Saves global plugin settings.
array __construct(protected EventsService $events, protected Translator $translator)
Constructor.
getConfiguredIntervals(bool $only_names=false)
Get the cron interval configuration.
c Accompany it with the information you received as to the offer to distribute corresponding source complete source code means all the source code for all modules it plus any associated interface definition plus the scripts used to control compilation and installation of the executable as a special the source code distributed need not include anything that is normally and so on of the operating system on which the executable unless that component itself accompanies the executable If distribution of executable or object code is made by offering access to copy from a designated then offering equivalent access to copy the source code from the same place counts as distribution of the source even though third parties are not compelled to copy the source along with the object code You may not or distribute the Program except as expressly provided under this License Any attempt otherwise to sublicense or distribute the Program is void
if(!$annotation instanceof ElggAnnotation) $time
trait TimeUsing
Adds methods for setting the current time (for testing)
Generic interface which allows catching of all exceptions thrown in Elgg.
getLastCompletion(string $interval)
Get the time of the last completion of a cron interval.
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
getCurrentTime($modifier= '')
Get the (cloned) time.
rotateLogs(string $interval)
Rotate the log files.
trait Loggable
Enables adding a logger.
getLogs(string $interval, bool $filenames_only=false)
Get the log files for a given cron interval.
execute(string $interval,\Elgg\Logger\Cron $cron_logger, string $filename,\DateTimeImmutable $time)
Execute handlers attached to a specific cron interval.
logCompletion(string $interval)
Log the completion time of a cron interval.
after(string $output, string $interval,\Elgg\Logger\Cron $cron_logger)
Printers handler result.
elgg_get_site_entity()
Get the current site entity.
getLogFilename(string $interval,\DateTimeInterface $time)
Get a filename to log in.
getLogger()
Returns logger.
Extension of the DateTime class to support formatting a date using the locale.
A generic parent class for cron exceptions.
before(string $interval,\Elgg\Logger\Cron $cron_logger,\DateTimeImmutable $time)
Execute commands before cron interval is run.
run(array $intervals=null, bool $force=false)
Executes handlers for periods that have elapsed since last cron.