38 $this->extConf = unserialize(
$GLOBALS[
'TYPO3_CONF_VARS'][
'EXT'][
'extConf'][
'scheduler']);
39 if (empty($this->extConf[
'maxLifetime'])) {
40 $this->extConf[
'maxLifetime'] = 1440;
42 if (empty($this->extConf[
'useAtdaemon'])) {
43 $this->extConf[
'useAtdaemon'] = 0;
56 $taskUid = $task->getTaskUid();
57 if (empty($taskUid)) {
60 'disable' => $task->isDisabled(),
61 'description' => $task->getDescription(),
62 'task_group' => $task->getTaskGroup(),
63 'serialized_task_object' =>
'RESERVED' 65 $result =
$GLOBALS[
'TYPO3_DB']->exec_INSERTquery(
'tx_scheduler_task', $fields);
67 $task->setTaskUid(
$GLOBALS[
'TYPO3_DB']->sql_insert_id());
90 $res =
$GLOBALS[
'TYPO3_DB']->exec_SELECTquery(
'uid, serialized_executions, serialized_task_object',
'tx_scheduler_task',
'serialized_executions <> \'\'');
91 $maxDuration = $this->extConf[
'maxLifetime'] * 60;
92 while ($row =
$GLOBALS[
'TYPO3_DB']->sql_fetch_assoc($res)) {
93 $executions = array();
94 if ($serialized_executions = unserialize($row[
'serialized_executions'])) {
95 foreach ($serialized_executions as $task) {
96 if ($tstamp - $task < $maxDuration) {
97 $executions[] = $task;
99 $task = unserialize($row[
'serialized_task_object']);
100 $logMessage =
'Removing logged execution, assuming that the process is dead. Execution of \'' . get_class($task) .
'\' (UID:
' . $row['uid
'] . ') was started at
' . date('Y-m-d H:i:s
', $task->getExecutionTime()); 101 $this->log($logMessage); 105 if (count($serialized_executions) != count($executions)) { 106 if (count($executions) == 0) { 109 $value = serialize($executions); 111 $GLOBALS['TYPO3_DB
']->exec_UPDATEquery('tx_scheduler_task
', 'uid =
' . (int)$row['uid
'], array('serialized_executions
' => $value)); 114 $GLOBALS['TYPO3_DB
']->sql_free_result($res); 124 public function executeTask(\TYPO3\CMS\Scheduler\Task\AbstractTask $task) { 125 // Trigger the saving of the task, as this will calculate its next execution time 126 // This should be calculated all the time, even if the execution is skipped 127 // (in case it is skipped, this pushes back execution to the next possible date) 129 // Set a scheduler object for the task again, 130 // as it was removed during the save operation 131 $task->setScheduler(); 133 // Task is already running and multiple executions are not allowed 134 if (!$task->areMultipleExecutionsAllowed() && $task->isExecutionRunning()) { 135 // Log multiple execution error 136 $logMessage = 'Task is already running and multiple executions are not allowed, skipping! Class:
' . get_class($task) . ', UID:
' . $task->getTaskUid(); 137 $this->log($logMessage); 140 // Log scheduler invocation 141 $logMessage = 'Start execution. Class:
' . get_class($task) . ', UID:
' . $task->getTaskUid(); 142 $this->log($logMessage); 143 // Register execution 144 $executionID = $task->markExecution(); 148 $successfullyExecuted = $task->execute(); 149 if (!$successfullyExecuted) { 150 throw new \TYPO3\CMS\Scheduler\FailedExecutionException('Task failed to execute successfully. Class:
' . get_class($task) . ', UID:
' . $task->getTaskUid(), 1250596541); 152 } catch (\Exception $e) { 153 // Store exception, so that it can be saved to database 156 // make sure database-connection is fine 157 // for long-running tasks the database might meanwhile have disconnected 158 $GLOBALS['TYPO3_DB
']->isConnected(); 159 // Un-register execution 160 $task->unmarkExecution($executionID, $failure); 161 // Log completion of execution 162 $logMessage = 'Task executed. Class:
' . get_class($task) . ', UID:
' . $task->getTaskUid(); 163 $this->log($logMessage); 164 // Now that the result of the task execution has been handled, 165 // throw the exception again, if any 166 if ($failure instanceof \Exception) { 179 public function recordLastRun($type = 'cron
') { 180 // Validate input value 181 if ($type !== 'manual
' && $type !== 'cli-by-
id') { 185 $registry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry
'); 186 $runInformation = array('start
' => $GLOBALS['EXEC_TIME
'], 'end
' => time(), 'type
' => $type); 187 $registry->set('tx_scheduler', 'lastRun
', $runInformation); 197 public function removeTask(\TYPO3\CMS\Scheduler\Task\AbstractTask $task) { 198 $taskUid = $task->getTaskUid(); 199 if (!empty($taskUid)) { 200 $result = $GLOBALS['TYPO3_DB
']->exec_DELETEquery('tx_scheduler_task
', 'uid =
' . $taskUid); 205 $this->scheduleNextSchedulerRunUsingAtDaemon(); 216 public function saveTask(\TYPO3\CMS\Scheduler\Task\AbstractTask $task) { 217 $taskUid = $task->getTaskUid(); 218 if (!empty($taskUid)) { 220 $executionTime = $task->getNextDueExecution(); 221 $task->setExecutionTime($executionTime); 222 } catch (\Exception $e) { 223 $task->setDisabled(TRUE); 226 $task->unsetScheduler(); 228 'nextexecution
' => $executionTime, 229 'disable
' => $task->isDisabled(), 230 'description
' => $task->getDescription(), 231 'task_group
' => $task->getTaskGroup(), 232 'serialized_task_object
' => serialize($task) 234 $result = $GLOBALS['TYPO3_DB
']->exec_UPDATEquery('tx_scheduler_task
', 'uid =
' . $taskUid, $fields); 239 $this->scheduleNextSchedulerRunUsingAtDaemon(); 252 public function fetchTask($uid = 0) { 253 // Define where clause 254 // If no uid is given, take any non-disabled task which has a next execution time in the past 257 'SELECT
' => 'tx_scheduler_task.uid AS uid, serialized_task_object
', 258 'FROM
' => 'tx_scheduler_task LEFT JOIN tx_scheduler_task_group ON tx_scheduler_task.task_group = tx_scheduler_task_group.uid
', 259 'WHERE
' => 'disable = 0 AND nextexecution != 0 AND nextexecution <=
' . $GLOBALS['EXEC_TIME
'] . ' AND (tx_scheduler_task_group.hidden = 0 OR tx_scheduler_task_group.hidden IS NULL)
', 264 'SELECT
' => 'uid, serialized_task_object
', 265 'FROM
' => 'tx_scheduler_task
', 266 'WHERE
' => 'uid =
' . (int)$uid, 271 $res = $GLOBALS['TYPO3_DB
']->exec_SELECT_queryArray($queryArray); 272 if ($res === FALSE) { 273 throw new \OutOfBoundsException('Query could not be executed. Possible defect in tables tx_scheduler_task or tx_scheduler_task_group or
DB server problems
', 1422044826); 275 // If there are no available tasks, thrown an exception 276 if ($GLOBALS['TYPO3_DB
']->sql_num_rows($res) == 0) { 277 throw new \OutOfBoundsException('No task
', 1247827244); 279 $row = $GLOBALS['TYPO3_DB
']->sql_fetch_assoc($res); 281 $task = unserialize($row['serialized_task_object
']); 282 if ($this->isValidTaskObject($task)) { 283 // The task is valid, return it 284 $task->setScheduler(); 286 // Forcibly set the disable flag to 1 in the database, 287 // so that the task does not come up again and again for execution 288 $GLOBALS['TYPO3_DB
']->exec_UPDATEquery('tx_scheduler_task
', 'uid =
' . $row['uid
'], array('disable
' => 1)); 289 // Throw an exception to raise the problem 290 throw new \UnexpectedValueException('Could not unserialize task
', 1255083671); 292 $GLOBALS['TYPO3_DB
']->sql_free_result($res); 305 public function fetchTaskRecord($uid) { 306 $res = $GLOBALS['TYPO3_DB
']->exec_SELECTquery('*
', 'tx_scheduler_task
', 'uid =
' . (int)$uid); 307 // If the task is not found, throw an exception 308 if ($GLOBALS['TYPO3_DB
']->sql_num_rows($res) == 0) { 309 throw new \OutOfBoundsException('No task
', 1247827245); 311 $row = $GLOBALS['TYPO3_DB
']->sql_fetch_assoc($res); 312 $GLOBALS['TYPO3_DB
']->sql_free_result($res); 325 public function fetchTasksWithCondition($where, $includeDisabledTasks = FALSE) { 328 if (!empty($where)) { 329 $whereClause = $where; 331 if (!$includeDisabledTasks) { 332 if (!empty($whereClause)) { 333 $whereClause .= ' AND
'; 335 $whereClause .= 'disable = 0
'; 337 $res = $GLOBALS['TYPO3_DB
']->exec_SELECTquery('serialized_task_object
', 'tx_scheduler_task
', $whereClause); 339 while ($row = $GLOBALS['TYPO3_DB
']->sql_fetch_assoc($res)) { 341 $task = unserialize($row['serialized_task_object
']); 342 // Add the task to the list only if it is valid 343 if ($this->isValidTaskObject($task)) { 344 $task->setScheduler(); 348 $GLOBALS['TYPO3_DB
']->sql_free_result($res); 365 public function isValidTaskObject($task) { 366 return $task instanceof \TYPO3\CMS\Scheduler\Task\AbstractTask; 378 public function log($message, $status = 0, $code = 'scheduler
') { 379 // Log only if enabled 380 if (!empty($this->extConf['enableBELog
'])) { 381 $GLOBALS['BE_USER
']->writelog(4, 0, $status, $code, '[scheduler]:
' . $message, array()); 392 public function scheduleNextSchedulerRunUsingAtDaemon() { 393 if ((int)$this->extConf['useAtdaemon
'] !== 1) { 397 $registry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Registry
'); 398 // Get at job id from registry and remove at job 400 if (\TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($atJobId)) { 401 shell_exec('atrm
' . (int)$atJobId . ' 2>&1
'); 403 // Can not use fetchTask() here because if tasks have just executed 404 // they are not in the list of next executions 405 $tasks = $this->fetchTasksWithCondition(''); 406 $nextExecution = FALSE; 407 foreach ($tasks as $task) { 410 $tempNextExecution = $task->getNextDueExecution(); 411 if ($nextExecution === FALSE || $tempNextExecution < $nextExecution) { 412 $nextExecution = $tempNextExecution; 414 } catch (\OutOfBoundsException $e) { 415 // The event will not be executed again or has already ended - we don't have to consider it
for 419 if ($nextExecution !== FALSE) {
420 if ($nextExecution >
$GLOBALS[
'EXEC_TIME']) {
421 $startTime = strftime(
'%H:%M %F', $nextExecution);
423 $startTime =
'now+1minute';
425 $cliDispatchPath = PATH_site .
'typo3/cli_dispatch.phpsh';
426 $currentLocale = setlocale(LC_CTYPE, 0);
427 if (
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'][
'UTF8filesystem']) {
428 setlocale(LC_CTYPE,
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'][
'systemLocale']);
430 $cmd =
'echo ' . escapeshellarg($cliDispatchPath) .
' scheduler | at ' . escapeshellarg($startTime) .
' 2>&1';
431 if (
$GLOBALS[
'TYPO3_CONF_VARS'][
'SYS'][
'UTF8filesystem']) {
432 setlocale(LC_CTYPE, $currentLocale);
434 $output = shell_exec($cmd);
436 foreach (explode(LF, $output) as $outputLine) {
437 if (\
TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($outputLine,
'job')) {
438 $outputParts = explode(
' ', $outputLine, 3);
442 if ($outputParts[0] ===
'job' && \
TYPO3\CMS\Core\Utility\MathUtility::canBeInterpretedAsInteger($outputParts[1])) {
443 $atJobId = (int)$outputParts[1];
444 $registry->set(
'tx_scheduler',
'atJobId', $atJobId);
if($list_of_literals) if(!empty($literals)) if(!empty($literals)) $result
Analyse literals to prepend the N char to them if their contents aren't numeric.
addTask(\TYPO3\CMS\Scheduler\Task\AbstractTask $task)
if(!defined('TYPO3_MODE')) $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['logoff_pre_processing'][]