6b3f9d35a2595cf0ab7055f41a9e93982e3b6abb
[Packages/TYPO3.CMS.git] / typo3 / sysext / scheduler / Classes / Task / AbstractTask.php
1 <?php
2 namespace TYPO3\CMS\Scheduler\Task;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * This is the base class for all Scheduler tasks
19 * It's an abstract class, not designed to be instantiated directly
20 * All Scheduler tasks should inherit from this class
21 *
22 * @author Fran├žois Suter <francois@typo3.org>
23 * @author Christian Jul Jensen <julle@typo3.org>
24 */
25 abstract class AbstractTask {
26
27 /**
28 * Reference to a scheduler object
29 *
30 * @var \TYPO3\CMS\Scheduler\Scheduler
31 */
32 protected $scheduler;
33
34 /**
35 * The unique id of the task used to identify it in the database
36 *
37 * @var int
38 */
39 protected $taskUid;
40
41 /**
42 * Disable flag, TRUE if task is disabled, FALSE otherwise
43 *
44 * @var bool
45 */
46 protected $disabled = FALSE;
47
48 /**
49 * The execution object related to the task
50 *
51 * @var \TYPO3\CMS\Scheduler\Execution
52 */
53 protected $execution;
54
55 /**
56 * This variable contains the time of next execution of the task
57 *
58 * @var int
59 */
60 protected $executionTime = 0;
61
62 /**
63 * Description for the task
64 *
65 * @var string
66 */
67 protected $description = '';
68
69 /**
70 * Task group for this task
71 *
72 * @var int
73 */
74 protected $taskGroup;
75
76 /**
77 * Constructor
78 */
79 public function __construct() {
80 $this->setScheduler();
81 $this->execution = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Scheduler\Execution::class);
82 }
83
84 /**
85 * This is the main method that is called when a task is executed
86 * It MUST be implemented by all classes inheriting from this one
87 * Note that there is no error handling, errors and failures are expected
88 * to be handled and logged by the client implementations.
89 * Should return TRUE on successful execution, FALSE on error.
90 *
91 * @return bool Returns TRUE on successful execution, FALSE on error
92 */
93 abstract public function execute();
94
95 /**
96 * This method is designed to return some additional information about the task,
97 * that may help to set it apart from other tasks from the same class
98 * This additional information is used - for example - in the Scheduler's BE module
99 * This method should be implemented in most task classes
100 *
101 * @return string Information to display
102 */
103 public function getAdditionalInformation() {
104 return '';
105 }
106
107 /**
108 * This method is used to set the unique id of the task
109 *
110 * @param int $id Primary key (from the database record) of the scheduled task
111 * @return void
112 */
113 public function setTaskUid($id) {
114 $this->taskUid = (int)$id;
115 }
116
117 /**
118 * This method returns the unique id of the task
119 *
120 * @return int The id of the task
121 */
122 public function getTaskUid() {
123 return $this->taskUid;
124 }
125
126 /**
127 * This method returns the title of the scheduler task
128 *
129 * @return string
130 */
131 public function getTaskTitle() {
132 return $GLOBALS['LANG']->sL($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][get_class($this)]['title']);
133 }
134
135 /**
136 * This method returns the description of the scheduler task
137 *
138 * @return string
139 */
140 public function getTaskDescription() {
141 return $GLOBALS['LANG']->sL($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][get_class($this)]['description']);
142 }
143
144 /**
145 * This method returns the class name of the scheduler task
146 *
147 * @return string
148 */
149 public function getTaskClassName() {
150 return get_class($this);
151 }
152
153 /**
154 * This method returns the disable status of the task
155 *
156 * @return bool TRUE if task is disabled, FALSE otherwise
157 */
158 public function isDisabled() {
159 return $this->disabled;
160 }
161
162 /**
163 * This method is used to set the disable status of the task
164 *
165 * @param bool $flag TRUE if task should be disabled, FALSE otherwise
166 * @return void
167 */
168 public function setDisabled($flag) {
169 if ($flag) {
170 $this->disabled = TRUE;
171 } else {
172 $this->disabled = FALSE;
173 }
174 }
175
176 /**
177 * This method is used to set the timestamp corresponding to the next execution time of the task
178 *
179 * @param int $timestamp Timestamp of next execution
180 * @return void
181 */
182 public function setExecutionTime($timestamp) {
183 $this->executionTime = (int)$timestamp;
184 }
185
186 /**
187 * This method returns the task group (uid) of the task
188 *
189 * @return int Uid of task group
190 */
191 public function getTaskGroup() {
192 return $this->taskGroup;
193 }
194
195 /**
196 * This method is used to set the task group (uid) of the task
197 *
198 * @param int $timestamp Uid of task group
199 * @return void
200 */
201 public function setTaskGroup($taskGroup) {
202 $this->taskGroup = (int)$taskGroup;
203 }
204
205 /**
206 * This method returns the timestamp corresponding to the next execution time of the task
207 *
208 * @return int Timestamp of next execution
209 */
210 public function getExecutionTime() {
211 return $this->executionTime;
212 }
213
214 /**
215 * This method is used to set the description of the task
216 *
217 * @param string $description Description
218 * @return void
219 */
220 public function setDescription($description) {
221 $this->description = $description;
222 }
223
224 /**
225 * This method returns the description of the task
226 *
227 * @return string Description
228 */
229 public function getDescription() {
230 return $this->description;
231 }
232
233 /**
234 * Sets the internal reference to the singleton instance of the Scheduler
235 *
236 * @return void
237 */
238 public function setScheduler() {
239 $this->scheduler = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Scheduler\Scheduler::class);
240 }
241
242 /**
243 * Unsets the internal reference to the singleton instance of the Scheduler
244 * This is done before a task is serialized, so that the scheduler instance
245 * is not saved to the database too
246 *
247 * @return void
248 */
249 public function unsetScheduler() {
250 unset($this->scheduler);
251 }
252
253 /**
254 * Registers a single execution of the task
255 *
256 * @param int $timestamp Timestamp of the next execution
257 */
258 public function registerSingleExecution($timestamp) {
259 /** @var $execution \TYPO3\CMS\Scheduler\Execution */
260 $execution = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Scheduler\Execution::class);
261 $execution->setStart($timestamp);
262 $execution->setInterval(0);
263 $execution->setEnd($timestamp);
264 $execution->setCronCmd('');
265 $execution->setMultiple(0);
266 $execution->setIsNewSingleExecution(TRUE);
267 // Replace existing execution object
268 $this->execution = $execution;
269 }
270
271 /**
272 * Registers a recurring execution of the task
273 *
274 * @param int $start The first date/time where this execution should occur (timestamp)
275 * @param string $interval Execution interval in seconds
276 * @param int $end The last date/time where this execution should occur (timestamp)
277 * @param bool $multiple Set to FALSE if multiple executions of this task are not permitted in parallel
278 * @param string $cron_cmd Used like in crontab (minute hour day month weekday)
279 * @return void
280 */
281 public function registerRecurringExecution($start, $interval, $end = 0, $multiple = FALSE, $cron_cmd = '') {
282 /** @var $execution \TYPO3\CMS\Scheduler\Execution */
283 $execution = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Scheduler\Execution::class);
284 // Set general values
285 $execution->setStart($start);
286 $execution->setEnd($end);
287 $execution->setMultiple($multiple);
288 if (empty($cron_cmd)) {
289 // Use interval
290 $execution->setInterval($interval);
291 $execution->setCronCmd('');
292 } else {
293 // Use cron syntax
294 $execution->setInterval(0);
295 $execution->setCronCmd($cron_cmd);
296 }
297 // Replace existing execution object
298 $this->execution = $execution;
299 }
300
301 /**
302 * Sets the internal execution object
303 *
304 * @param \TYPO3\CMS\Scheduler\Execution $execution The execution to add
305 */
306 public function setExecution(\TYPO3\CMS\Scheduler\Execution $execution) {
307 $this->execution = $execution;
308 }
309
310 /**
311 * Returns the execution object
312 *
313 * @return \TYPO3\CMS\Scheduler\Execution The internal execution object
314 */
315 public function getExecution() {
316 return $this->execution;
317 }
318
319 /**
320 * Returns the timestamp for next due execution of the task
321 *
322 * @return int Date and time of the next execution as a timestamp
323 */
324 public function getNextDueExecution() {
325 // NOTE: this call may throw an exception, but we let it bubble up
326 return $this->execution->getNextExecution();
327 }
328
329 /**
330 * Returns TRUE if several runs of the task are allowed concurrently
331 *
332 * @return bool TRUE if concurrent executions are allowed, FALSE otherwise
333 */
334 public function areMultipleExecutionsAllowed() {
335 return $this->execution->getMultiple();
336 }
337
338 /**
339 * Returns TRUE if an instance of the task is already running
340 *
341 * @return bool TRUE if an instance is already running, FALSE otherwise
342 */
343 public function isExecutionRunning() {
344 $isRunning = FALSE;
345 $queryArr = array(
346 'SELECT' => 'serialized_executions',
347 'FROM' => 'tx_scheduler_task',
348 'WHERE' => 'uid = ' . $this->taskUid,
349 'LIMIT' => 1
350 );
351 $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArr);
352 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
353 if (strlen($row['serialized_executions']) > 0) {
354 $isRunning = TRUE;
355 }
356 }
357 $GLOBALS['TYPO3_DB']->sql_free_result($res);
358 return $isRunning;
359 }
360
361 /**
362 * This method adds current execution to the execution list
363 * It also logs the execution time and mode
364 *
365 * @return int Execution id
366 */
367 public function markExecution() {
368 $queryArr = array(
369 'SELECT' => 'serialized_executions',
370 'FROM' => 'tx_scheduler_task',
371 'WHERE' => 'uid = ' . $this->taskUid,
372 'LIMIT' => 1
373 );
374 $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArr);
375 $runningExecutions = array();
376 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
377 if (strlen($row['serialized_executions']) > 0) {
378 $runningExecutions = unserialize($row['serialized_executions']);
379 }
380 }
381 $GLOBALS['TYPO3_DB']->sql_free_result($res);
382 // Count the number of existing executions and use that number as a key
383 // (we need to know that number, because it is returned at the end of the method)
384 $numExecutions = count($runningExecutions);
385 $runningExecutions[$numExecutions] = time();
386 // Define the context in which the script is running
387 $context = 'BE';
388 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
389 $context = 'CLI';
390 }
391 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_scheduler_task', 'uid = ' . $this->taskUid, array(
392 'serialized_executions' => serialize($runningExecutions),
393 'lastexecution_time' => time(),
394 'lastexecution_context' => $context
395 ));
396 return $numExecutions;
397 }
398
399 /**
400 * Removes given execution from list
401 *
402 * @param int $executionID Id of the execution to remove.
403 * @param \Exception $failure An exception to signal a failed execution
404 * @return void
405 */
406 public function unmarkExecution($executionID, \Exception $failure = NULL) {
407 // Get the executions for the task
408 $queryArr = array(
409 'SELECT' => 'serialized_executions',
410 'FROM' => 'tx_scheduler_task',
411 'WHERE' => 'uid = ' . $this->taskUid,
412 'LIMIT' => 1
413 );
414 $res = $GLOBALS['TYPO3_DB']->exec_SELECT_queryArray($queryArr);
415 if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
416 if (strlen($row['serialized_executions']) > 0) {
417 $runningExecutions = unserialize($row['serialized_executions']);
418 // Remove the selected execution
419 unset($runningExecutions[$executionID]);
420 if (count($runningExecutions) > 0) {
421 // Re-serialize the updated executions list (if necessary)
422 $runningExecutionsSerialized = serialize($runningExecutions);
423 } else {
424 $runningExecutionsSerialized = '';
425 }
426 if ($failure instanceof \Exception) {
427 // Log failed execution
428 $logMessage = 'Task failed to execute successfully. Class: ' . get_class($this) . ', UID: ' . $this->taskUid . '. ' . $failure->getMessage();
429 $this->scheduler->log($logMessage, 1, $failure->getCode());
430 $failure = serialize($failure);
431 } else {
432 $failure = '';
433 }
434 // Save the updated executions list
435 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_scheduler_task', 'uid = ' . $this->taskUid, array(
436 'serialized_executions' => $runningExecutionsSerialized,
437 'lastexecution_failure' => $failure
438 ));
439 }
440 }
441 $GLOBALS['TYPO3_DB']->sql_free_result($res);
442 }
443
444 /**
445 * Clears all marked executions
446 *
447 * @return bool TRUE if the clearing succeeded, FALSE otherwise
448 */
449 public function unmarkAllExecutions() {
450 // Set the serialized executions field to empty
451 $result = $GLOBALS['TYPO3_DB']->exec_UPDATEquery('tx_scheduler_task', 'uid = ' . $this->taskUid, array(
452 'serialized_executions' => ''
453 ));
454 return $result;
455 }
456
457 /**
458 * Saves the details of the task to the database.
459 *
460 * @return bool
461 */
462 public function save() {
463 return $this->scheduler->saveTask($this);
464 }
465
466 /**
467 * Stops the task, by replacing the execution object by an empty one
468 * NOTE: the task still needs to be saved after that
469 *
470 * @return void
471 */
472 public function stop() {
473 $this->execution = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Scheduler\Execution::class);
474 }
475
476 /**
477 * Removes the task totally from the system.
478 *
479 * @return void
480 */
481 public function remove() {
482 $this->scheduler->removeTask($this);
483 }
484
485 }