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 *',
48 $this->events = $events;
49 $this->translator = $translator;
61 public function run(array $intervals = null,
bool $force =
false): array {
62 if (!isset($intervals)) {
63 $intervals = array_keys($this->default_intervals);
66 $allowed_intervals = $this->getConfiguredIntervals();
68 $scheduler =
new Scheduler();
71 foreach ($intervals as $interval) {
72 if (!array_key_exists($interval, $allowed_intervals)) {
73 throw new CronException(
"{$interval} is not a recognized cron interval. Please use one of the following: " .
implode(
', ', array_keys($allowed_intervals)));
76 $cron_interval = $force ? $allowed_intervals[
'minute'] : $allowed_intervals[$interval];
79 $cron_logger = \Elgg\Logger\Cron::factory([
80 'interval' => $interval,
85 ->call(
function () use ($interval,
$time, $cron_logger,
$filename) {
89 ->before(
function () use ($interval,
$time, $cron_logger) {
90 $this->before($interval, $cron_logger,
$time);
92 ->then(
function (
$output) use ($interval, $cron_logger) {
93 $this->after(
$output, $interval, $cron_logger);
97 return $scheduler->run(
$time);
115 $this->events->triggerBefore(
'cron', $interval,
$time);
116 }
catch (\Throwable $t) {
121 set_time_limit((
int) ini_get(
'max_execution_time'));
125 $cron_logger->notice($this->translator->translate(
'admin:cron:started', [$interval,
$time->format(DATE_RFC2822)]));
126 $cron_logger->notice($this->translator->translate(
'admin:cron:started:actual', [$interval, $now->format(DATE_RFC2822)]));
147 $begin_callback =
function (array
$params) use ($cron_logger) {
150 $cron_logger->notice(
"Starting {$readable_callable}");
153 $end_callback =
function (array
$params) use ($cron_logger) {
156 $cron_logger->notice(
"Finished {$readable_callable}");
159 $old_stdout = $this->events->triggerResults(
'cron', $interval, [
160 'time' =>
$time->getTimestamp(),
162 'logger' => $cron_logger,
164 EventsService::OPTION_BEGIN_CALLBACK => $begin_callback,
165 EventsService::OPTION_END_CALLBACK => $end_callback,
168 $ob_output = ob_get_clean();
170 if (!empty($ob_output)) {
171 elgg_deprecated_notice(
'Direct output (echo, print) in a CRON event will be removed, use the provided "logger"',
'5.1');
173 $cron_logger->notice($ob_output, [
'ob_output']);
176 if (!empty($old_stdout)) {
177 elgg_deprecated_notice(
'Output in a CRON event result will be removed, use the provided "logger"',
'5.1');
179 $cron_logger->notice($old_stdout, [
'event_result']);
181 }
catch (\Throwable $t) {
182 $ob_output = ob_get_clean();
184 if (!empty($ob_output)) {
185 elgg_deprecated_notice(
'Direct output (echo, print) in a CRON event will be removed, use the provided "logger"',
'5.1');
187 $cron_logger->notice($ob_output, [
'ob_output',
'throwable']);
195 $complete = $this->translator->translate(
'admin:cron:complete', [$interval, $now->format(DATE_RFC2822)]);
196 $cron_logger->notice($complete);
198 if (file_exists($filename) && is_readable($filename)) {
199 return file_get_contents($filename);
218 $this->events->triggerAfter(
'cron', $interval,
new \
DateTime());
219 }
catch (\Throwable $t) {
223 $cron_logger->close();
224 $this->rotateLogs($interval);
225 $this->logCompletion($interval);
238 public function getLogs(
string $interval,
bool $filenames_only =
false): array {
239 $fh = new \ElggFile();
241 $fh->setFilename(
"cron/{$interval}/dummy.log");
243 $dir = pathinfo($fh->getFilenameOnFilestore(), PATHINFO_DIRNAME);
244 if (!is_dir($dir) || !is_readable($dir)) {
248 $dh = new \DirectoryIterator($dir);
251 foreach ($dh as $file) {
252 if ($file->isDot() || !$file->isFile()) {
256 if ($filenames_only) {
257 $files[] = $file->getFilename();
259 $files[$file->getFilename()] = file_get_contents($file->getPathname());
263 if ($filenames_only) {
266 uksort($files,
'strnatcasecmp');
269 return array_reverse($files);
280 $fh = new \ElggFile();
282 $fh->setFilename(
"cron/{$interval}.complete");
284 if (!$fh->exists()) {
288 $date = $fh->grabFile();
295 return Values::normalizeTime($date);
312 $result = $this->events->triggerResults(
'cron:intervals',
'system', [], $this->default_intervals);
314 $this->
getLogger()->warning(
"The event 'cron:intervals', 'system' should return an array, " . gettype(
$result) .
' given');
316 $result = $this->default_intervals;
339 $date =
$time->format(\DateTimeInterface::ATOM);
341 $date = preg_replace(
'/[^a-zA-Z0-9_-]+/',
'-', $date);
343 $fh = new \ElggFile();
345 $fh->setFilename(
"cron/{$interval}/{$date}.log");
347 return $fh->getFilenameOnFilestore();
358 $files = $this->getLogs($interval,
true);
359 if (
count($files) <= self::LOG_FILES_TO_KEEP) {
363 $fh = new \ElggFile();
366 while (
count($files) > self::LOG_FILES_TO_KEEP) {
369 $fh->setFilename(
"cron/{$interval}/{$filename}");
382 $fh = new \ElggFile();
384 $fh->setFilename(
"cron/{$interval}.complete");
387 if ($fh->open(
'write') ===
false) {
396 $fh->write($now->format(\DateTimeInterface::ATOM));
__construct(EventsService $events, Translator $translator)
Constructor.
$params
Saves global plugin settings.
elgg_deprecated_notice(string $msg, string $dep_version)
Log a notice about deprecated use of a function, view, etc.
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.
before(string $interval,\Elgg\Logger\Cron $cron_logger,\DateTime $time=null)
Execute commands before cron interval is run.
elgg_extract($key, $array, $default=null, bool $strict=true)
Checks for $array[$key] and returns its value if it exists, else returns $default.
getLogFilename(string $interval,\DateTime $time=null)
Get a filename to log in.
getCurrentTime($modifier= '')
Get the (cloned) time.
rotateLogs(string $interval)
Rotate the log files.
trait Loggable
Enables adding a logger.
array EventsService $events
getLogs(string $interval, bool $filenames_only=false)
Get the log files for a given 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.
getLogger()
Returns logger.
execute(string $interval,\Elgg\Logger\Cron $cron_logger, string $filename,\DateTime $time=null)
Execute handlers attached to a specific cron interval.
A generic parent class for cron exceptions.
run(array $intervals=null, bool $force=false)
Executes handlers for periods that have elapsed since last cron.