Passed
Push — master ( c0a3a7...3b84a4 )
by Jeroen
58:51
created

engine/classes/Elgg/Cron.php (1 issue)

1
<?php
2
3
namespace Elgg;
4
5
use DateTime;
6
use GO\Job;
7
use GO\Scheduler;
8
use Zend\Validator\Date;
9
10
/**
11
 * Cron
12
 *
13
 * @internal
14
 */
15
class Cron {
16
17
	use TimeUsing;
18
19
	static $intervals = [
20
		'minute' => '* * * * *',
21
		'fiveminute' => '*/5 * * * *',
22
		'fifteenmin' => '*/15 * * * *',
23
		'halfhour' => '*/30 * * * *',
24
		'hourly' => '0 * * * *',
25
		'daily' => '0 0 * * *',
26
		'weekly' => '0 0 * * 0',
27
		'monthly' => '0 0 1 * *',
28
		'yearly' => '0 0 1 1 *',
29
	];
30
31
	/**
32
	 * @var PluginHooksService
33
	 */
34
	protected $hooks;
35
36
	/**
37
	 * @var Printer
38
	 */
39
	protected $printer;
40
41
	/**
42
	 * Constructor
43
	 *
44
	 * @param PluginHooksService $hooks   Hooks service
45
	 * @param Printer            $printer Printer
46
	 */
47 7
	public function __construct(PluginHooksService $hooks, Printer $printer) {
48 7
		$this->hooks = $hooks;
49 7
		$this->printer = $printer;
50 7
	}
51
52
	/**
53
	 * Executes handlers for periods that have elapsed since last cron
54
	 *
55
	 * @param array $intervals Interval names to run
56
	 *
57
	 * @return Job[]
58
	 * @throws \CronException
59
	 */
60 6
	public function run(array $intervals = null) {
61
62 6
		if (!isset($intervals)) {
63 2
			$intervals = array_keys(self::$intervals);
64
		}
65
66 6
		$scheduler = new Scheduler();
67
68 6
		$time = $this->getCurrentTime();
69
70 6
		foreach ($intervals as $interval) {
71 6
			if (!array_key_exists($interval, self::$intervals)) {
72 1
				throw new \CronException("$interval is not a recognized cron interval");
73
			}
74
75
			$scheduler
76 5
				->call(function () use ($interval, $time) {
77 5
					return $this->execute($interval, $time);
78 5
				})
79 5
				->at(self::$intervals[$interval])
80 5
				->before(function () use ($interval, $time) {
81 5
					$this->before($interval, $time);
82 5
				})
83 5
				->then(function ($output) use ($interval) {
84 5
					$this->after($output, $interval);
85 5
				});
86
		}
87
88 5
		return $scheduler->run($time);
89
	}
90
91
	/**
92
	 * Execute commands before cron interval is run
93
	 *
94
	 * @param string   $interval Interval name
95
	 * @param DateTime $time     Time of the cron initialization
96
	 *
97
	 * @return void
98
	 */
99 5
	protected function before($interval, DateTime $time = null) {
100
101 5
		if (!isset($time)) {
102
			$time = $this->getCurrentTime();
103
		}
104
105 5
		$this->hooks->getEvents()->triggerBefore('cron', $interval, $time);
106
107
		// give every period at least 'max_execution_time' (PHP ini setting)
108 5
		set_time_limit((int) ini_get('max_execution_time'));
109
110 5
		$formatted_time = $time->format('r');
111
112 5
		$msg = elgg_echo('admin:cron:started', [$interval, $formatted_time]) . PHP_EOL;
113
114 5
		$this->log('output', $interval, $msg);
115 5
	}
116
117
	/**
118
	 * Execute handlers attached to a specific cron interval
119
	 *
120
	 * @param string   $interval Cron interval to execute
121
	 * @param DateTime $time     Time of cron initialization
122
	 *
123
	 * @return string
124
	 */
125 5
	protected function execute($interval, DateTime $time = null) {
126
127 5
		if (!isset($time)) {
128
			$time = $this->getCurrentTime();
129
		}
130
131 5
		$output = [];
132
133 5
		$output[] = elgg_echo('admin:cron:started', [$interval, $time->format('r')]);
134
135 5
		ob_start();
136
137 5
		$old_stdout = $this->hooks->trigger('cron', $interval, [
138 5
			'time' => $time->getTimestamp(),
139 5
			'dt' => $time,
140 5
		], '');
141
142 5
		$output[] = ob_get_clean();
143 5
		$output[] = $old_stdout;
144
145 5
		$time = $this->getCurrentTime();
146
147 5
		$output[] = elgg_echo('admin:cron:complete', [$interval, $time->format('r')]);
148
149 5
		return implode(PHP_EOL, array_filter($output));
150
	}
151
152
	/**
153
	 * Printers handler result
154
	 *
155
	 * @param string $output   Output string
156
	 * @param string $interval Interval name
157
	 *
158
	 * @return void
159
	 */
160 5
	protected function after($output, $interval) {
161
162 5
		$time = new \DateTime();
163
164 5
		$this->log('output', $interval, $output);
165 5
		$this->log('completion', $interval, $time->getTimestamp());
166
167 5
		$this->printer->write($output, null, Logger::$verbosity);
0 ignored issues
show
The call to Elgg\Printer::write() has too many arguments starting with Elgg\Logger::verbosity. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

167
		$this->printer->/** @scrutinizer ignore-call */ 
168
                  write($output, null, Logger::$verbosity);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
168
169 5
		$this->hooks->getEvents()->triggerAfter('cron', $interval, $time);
170 5
	}
171
172
	/**
173
	 * Populate sites private settings with cron info
174
	 *
175
	 * @param string $setting  'output'|'completion'
176
	 * @param string $interval Interval name
177
	 * @param string $msg      Logged message
178
	 *
179
	 * @return void
180
	 */
181 5
	protected function log($setting, $interval, $msg = '') {
182 5
		$suffix = $setting === 'completion' ? 'ts' : 'msg';
183 5
		$msg_key = "cron_latest:$interval:$suffix";
184
185 5
		elgg_get_site_entity()->setPrivateSetting($msg_key, $msg);
186 5
	}
187
188
}
189