‪TYPO3CMS  ‪main
Scheduler.php
Go to the documentation of this file.
1 <?php
2 
3 /*
4  * This file is part of the TYPO3 CMS project.
5  *
6  * It is free software; you can redistribute it and/or modify it under
7  * the terms of the GNU General Public License, either version 2
8  * of the License, or any later version.
9  *
10  * For the full copyright and license information, please read the
11  * LICENSE.txt file that was distributed with this source code.
12  *
13  * The TYPO3 project - inspiring people to share!
14  */
15 
16 namespace ‪TYPO3\CMS\Scheduler;
17 
18 use Psr\Log\LoggerInterface;
29 
34 {
35  protected LoggerInterface ‪$logger;
38 
42  public array ‪$extConf = [];
43 
48  {
49  $this->logger = ‪$logger;
50  $this->taskSerializer = ‪$taskSerializer;
51  $this->schedulerTaskRepository = ‪$schedulerTaskRepository;
52  // Get configuration from the extension manager
53  $this->extConf = GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('scheduler');
54  if (empty($this->extConf['maxLifetime'])) {
55  $this->extConf['maxLifetime'] = 1440;
56  }
57  // Clean up the serialized execution arrays
58  $this->‪cleanExecutionArrays();
59  }
60 
65  protected function ‪cleanExecutionArrays()
66  {
67  $tstamp = ‪$GLOBALS['EXEC_TIME'];
68  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
69  $queryBuilder = $connectionPool->getQueryBuilderForTable('tx_scheduler_task');
70 
71  // Select all tasks with executions
72  // NOTE: this cleanup is done for disabled tasks too,
73  // to avoid leaving old executions lying around
74  $result = $queryBuilder->select('uid', 'serialized_executions', 'serialized_task_object')
75  ->from('tx_scheduler_task')
76  ->where(
77  $queryBuilder->expr()->neq(
78  'serialized_executions',
79  $queryBuilder->createNamedParameter('')
80  ),
81  $queryBuilder->expr()->eq('deleted', $queryBuilder->createNamedParameter(0, ‪Connection::PARAM_INT))
82  )
83  ->executeQuery();
84  $maxDuration = $this->extConf['maxLifetime'] * 60;
85  while ($row = $result->fetchAssociative()) {
86  $executions = [];
87  if ($serialized_executions = unserialize($row['serialized_executions'])) {
88  foreach ($serialized_executions as $task) {
89  if ($tstamp - $task < $maxDuration) {
90  $executions[] = $task;
91  } else {
92  try {
93  $schedulerTask = $this->taskSerializer->deserialize($row['serialized_task_object']);
94  $taskClass = get_class($schedulerTask);
95  $executionTime = date('Y-m-d H:i:s', $schedulerTask->getExecutionTime());
96  } catch (‪InvalidTaskException $e) {
97  $taskClass = 'unknown class';
98  $executionTime = 'unknown time';
99  }
100  $this->logger->info(
101  'Removing logged execution, assuming that the process is dead. Execution of \'{taskClass} \' (UID: {taskId}) was started at {executionTime}',
102  [
103  'taskClass' => $taskClass,
104  'taskId' => $row['uid'],
105  'executionTime' => $executionTime,
106  ]
107  );
108  }
109  }
110  }
111  $executionCount = count($executions);
112  if (!is_array($serialized_executions) || count($serialized_executions) !== $executionCount) {
113  if ($executionCount === 0) {
114  $value = '';
115  } else {
116  $value = serialize($executions);
117  }
118  $connectionPool->getConnectionForTable('tx_scheduler_task')->update(
119  'tx_scheduler_task',
120  ['serialized_executions' => $value],
121  ['uid' => (int)$row['uid']],
122  ['serialized_executions' => ‪Connection::PARAM_LOB]
123  );
124  }
125  }
126  }
127 
136  public function ‪executeTask(‪AbstractTask $task)
137  {
138  $task->‪setRunOnNextCronJob(false);
139  // Trigger the saving of the task, as this will calculate its next execution time
140  // This should be calculated all the time, even if the execution is skipped
141  // (in case it is skipped, this pushes back execution to the next possible date)
142  $this->schedulerTaskRepository->update($task);
143  // Set a scheduler object for the task again,
144  // as it was removed during the save operation
145  $task->‪setScheduler();
146  $result = true;
147  // Task is already running and multiple executions are not allowed
148  if (!$task->‪areMultipleExecutionsAllowed() && $this->schedulerTaskRepository->isTaskMarkedAsRunning($task)) {
149  // Log multiple execution error
150  $this->logger->info('Task is already running and multiple executions are not allowed, skipping! Class: {class}, UID: {uid}', [
151  'class' => get_class($task),
152  'uid' => $task->‪getTaskUid(),
153  ]);
154  $result = false;
155  } else {
156  // Log scheduler invocation
157  $this->logger->info('Start execution. Class: {class}, UID: {uid}', [
158  'class' => get_class($task),
159  'uid' => $task->‪getTaskUid(),
160  ]);
161  // Register execution
162  $executionID = $this->schedulerTaskRepository->addExecutionToTask($task);
163  $failureString = '';
164  $e = null;
165  try {
166  // Execute task
167  $successfullyExecuted = $task->‪execute();
168  if (!$successfullyExecuted) {
169  throw new ‪FailedExecutionException('Task failed to execute successfully. Class: ' . get_class($task) . ', UID: ' . $task->‪getTaskUid(), 1250596541);
170  }
171  } catch (\Throwable $e) {
172  // Log failed execution
173  $this->logger->error('Task failed to execute successfully. Class: {class}, UID: {uid}', [
174  'class' => get_class($task),
175  'uid' => $task->‪getTaskUid(),
176  'exception' => $e,
177  ]);
178  // Store exception, so that it can be saved to database
179  // Do not serialize the complete exception or the trace, this can lead to huge strings > 50MB
180  $failureString = serialize([
181  'code' => $e->getCode(),
182  'message' => $e->getMessage(),
183  'file' => $e->getFile(),
184  'line' => $e->getLine(),
185  'traceString' => $e->getTraceAsString(),
186  ]);
187  }
188  // Un-register execution
189  $this->schedulerTaskRepository->removeExecutionOfTask($task, $executionID, $failureString);
190  // Log completion of execution
191  $this->logger->info('Task executed. Class: {class}, UID: {uid}', [
192  'class' => get_class($task),
193  'uid' => $task->‪getTaskUid(),
194  ]);
195  // Now that the result of the task execution has been handled,
196  // throw the exception again, if any
197  if ($e !== null) {
198  throw $e;
199  }
200  }
201  return $result;
202  }
203 
209  public function ‪recordLastRun($type = 'cron')
210  {
211  // Validate input value
212  if ($type !== 'manual' && $type !== 'cli-by-id') {
213  $type = 'cron';
214  }
215  $registry = GeneralUtility::makeInstance(Registry::class);
216  $runInformation = ['start' => ‪$GLOBALS['EXEC_TIME'], 'end' => time(), 'type' => $type];
217  $registry->set('tx_scheduler', 'lastRun', $runInformation);
218  }
219 }
‪TYPO3\CMS\Scheduler\Scheduler\$taskSerializer
‪TaskSerializer $taskSerializer
Definition: Scheduler.php:36
‪TYPO3\CMS\Scheduler
Definition: AbstractAdditionalFieldProvider.php:18
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:52
‪TYPO3\CMS\Scheduler\Task\AbstractTask\getTaskUid
‪int getTaskUid()
Definition: AbstractTask.php:134
‪TYPO3\CMS\Core\Configuration\ExtensionConfiguration
Definition: ExtensionConfiguration.php:47
‪TYPO3\CMS\Scheduler\FailedExecutionException
Definition: FailedExecutionException.php:21
‪TYPO3\CMS\Core\Registry
Definition: Registry.php:33
‪TYPO3\CMS\Scheduler\Domain\Repository\SchedulerTaskRepository
Definition: SchedulerTaskRepository.php:36
‪TYPO3\CMS\Scheduler\Scheduler\executeTask
‪bool executeTask(AbstractTask $task)
Definition: Scheduler.php:136
‪TYPO3\CMS\Scheduler\Task\AbstractTask\setRunOnNextCronJob
‪setRunOnNextCronJob($flag)
Definition: AbstractTask.php:198
‪TYPO3\CMS\Scheduler\Scheduler\$extConf
‪array $extConf
Definition: Scheduler.php:42
‪TYPO3\CMS\Scheduler\Task\TaskSerializer
Definition: TaskSerializer.php:28
‪TYPO3\CMS\Scheduler\Task\AbstractTask\setScheduler
‪setScheduler()
Definition: AbstractTask.php:278
‪TYPO3\CMS\Scheduler\Scheduler\recordLastRun
‪recordLastRun($type='cron')
Definition: Scheduler.php:209
‪TYPO3\CMS\Scheduler\Scheduler\$logger
‪LoggerInterface $logger
Definition: Scheduler.php:35
‪TYPO3\CMS\Scheduler\Task\AbstractTask
Definition: AbstractTask.php:33
‪TYPO3\CMS\Scheduler\Scheduler\__construct
‪__construct(LoggerInterface $logger, TaskSerializer $taskSerializer, SchedulerTaskRepository $schedulerTaskRepository)
Definition: Scheduler.php:47
‪TYPO3\CMS\Scheduler\Scheduler
Definition: Scheduler.php:34
‪TYPO3\CMS\Scheduler\Task\AbstractTask\areMultipleExecutionsAllowed
‪bool areMultipleExecutionsAllowed()
Definition: AbstractTask.php:386
‪TYPO3\CMS\Scheduler\Scheduler\cleanExecutionArrays
‪cleanExecutionArrays()
Definition: Scheduler.php:65
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:41
‪TYPO3\CMS\Scheduler\Task\AbstractTask\execute
‪bool execute()
‪TYPO3\CMS\Core\SingletonInterface
Definition: SingletonInterface.php:22
‪TYPO3\CMS\Scheduler\Scheduler\$schedulerTaskRepository
‪SchedulerTaskRepository $schedulerTaskRepository
Definition: Scheduler.php:37
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Scheduler\Exception\InvalidTaskException
Definition: InvalidTaskException.php:26
‪TYPO3\CMS\Core\Database\Connection\PARAM_LOB
‪const PARAM_LOB
Definition: Connection.php:62