Creating a new task¶
Creating the task class¶
This is the heart of a task. It is the code that actually does what the task is supposed to do. A task is represented by a PHP class that extends the base task class:
namespace Vendor\Extension\Task;
class MyTask extends \TYPO3\CMS\Scheduler\Task\AbstractTask {
public function execute() {
...
}
}
The only method that must be implemented is the execute()
method which is expected to perform the task logic. The method must
return a boolean value of “true” if the execution was successful, and
“false” otherwise. It may also throw exceptions to report more
precisely on errors that may happen during the run. The message of the
exception is stored in the database so that it can be displayed in the
BE module.
A method called getAdditionalInformation()
may optionally be
implemented too. It is called by the BE module to display additional
information about a registered task in the list view. This is quite
convenient if a task class may be registered several times with
different parameters, in order to tell apart the various
registrations.
If the task class uses a constructor it is absolutely necessary to include a call to the parent’s constructor, most probably as the first thing inside the constructor, unless there are some very special reasons not to:
namespace Vendor\Extension\Task;
class MyTask extends \TYPO3\CMS\Scheduler\Task\AbstractTask {
public function __construct() {
parent::__construct();
// Your code here...
}
}
Using additional fields¶
A task class may require additional parameters to be executed properly. For example, the “test” task is designed to send an e-mail, but the e-mail address must be defined at the time when the task class is actually registered. This appears as an additional field in the task registration form. The Scheduler provides an interface which may be implemented to provide such fields. It is comprised of three methods, which must all be implemented (since it’s an interface).
The implementation may be done in a separate class or in the same class as the task. It may look something like:
namespace Vendor\Extension\Task;
class MyTaskAdditionalFieldProvider implements \TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface {
public function getAdditionalFields(array &$taskInfo, $task, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject) {
...
}
public function validateAdditionalFields(array &$submittedData, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject) {
...
}
public function saveAdditionalFields(array $submittedData, \TYPO3\CMS\Scheduler\Task\AbstractTask $task) {
...
}
}
The three methods of the interface are described below:
getAdditionalFields¶
Method
getAdditionalFields
Purpose
This method returns the list of fields to display in the editing form.
This list of fields is a 2-dimensional array. The first dimension uses the ID of the field (as used in the “id” attribute of the field tag). Then for each field, there must be the HTML code to render the field, the label of the field, the key and the label for the context- sensitive help (CSH).
All this is documented in the
\TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface
interface and can be
seen in action in the existing task classes.
Parameters
$taskInfo
: an array containing the information about the
current task. May be modified inside this method to set default
values, for example.
$task
: the current task object (when editing; when adding,
“null” is passed to the method).
$parentObject
: a back-reference to the calling BE module’s
object.
validateAdditionalFields¶
Method
validateAdditionalFields
Purpose
This method is called to validate the values that were input in the additional fields provided by the specific task. It is expected to return false if any of the fields contained errors, true otherwise.
The method should use the parent object’s addMessage()
method
to output messages about validation errors.
Parameters
$submittedData
: array of values from the submitted form. May
be modified inside the method.
$parentObject
: a back-reference to the calling BE module’s
object.
saveAdditionalFields¶
Method
saveAdditionalFields
Purpose
This method is used to store the values contained in the additional fields. The simplest method is to simply assign them to member variables, so that they will be stored along the serialized task object in the database (see Appendix A – Technical Background).
Parameters
$submittedData
: array of values from the submitted form,
after validation.
$task
: the current task object.
Security issues¶
The handling of additional fields is entirely up to the developer, in particular as far as validation and display is concerned. Thus it is up to you to make sure that the data entered in your additional fields does not contain any harmful input (for example, XSS).
The safest way to proceed is to very strictly handle input. For
example, if all you expect is some integer number, pass the value
through a typecast to (int). If it’s a string, but you don’t
expect any markup in it, run it through strip\_tags()
. Such
simple measures can filter most of the harmful code.
One more thing to mind. In the getAdditionalFields()
method,
the whole form element must be assembled. If the input didn’t
validate, you may well want to insert it in the field again, so that
the user can correct it. Harmful code entered at that point may be
executed. It is thus very strongly recommended to use
htmlspecialchars()
in this case. Example:
$fieldCode = '<input type="text" class="form-control" name="tx_scheduler[email]" id="' . $fieldID . '" value="' . htmlspecialchars($taskInfo['email']) . '" size="30">';
Naming of additional fields¶
The Scheduler expects all fields in the add/edit form to be named
tx\_scheduler[...]
. Values from fields that don’t follow this
pattern will not appear in the $submittedData
array.
It is also important to think about using field names that will not create conflicts with other existing fields or future fields that may happen. Since there is no name-spacing mechanism available, it is up to each developer to choose proper names. A very good practice is to prepend the extension’s key to the additional fields names in order to guarantee the unicity of those names.
Bad examples¶
Here are some examples of bad additional fields names:
foo
tx_scheduler[name]
The first name doesn’t use the syntax and thus will not be handled by the Scheduler. The second one respects that syntax, but the name is too generic and may cause conflicts with other fields.
Good examples¶
Here are good examples of additional fields names:
tx_scheduler[myextension_myvalue]
tx_scheduler[myextension][myvalue]
In both cases the proper syntax is used as well as the extension key, making it very unlikely that a naming conflict could happen.
Declaring the task class¶
As a last step, the task class must be declared so the Scheduler knows
of its existence. The declaration must be placed in the
ext\_localconf.php
file of the extension that provides the
task. Let’s look at one of the base classes declaration as an example:
// Add caching framework garbage collection task
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\CachingFrameworkGarbageCollectionTask::class] = array(
'extension' => $_EXTKEY,
'title' => 'LLL:EXT:' . $_EXTKEY . '/locallang.xlf:cachingFrameworkGarbageCollection.name',
'description' => 'LLL:EXT:' . $_EXTKEY . '/locallang.xlf:cachingFrameworkGarbageCollection.description',
'additionalFields' => \TYPO3\CMS\Scheduler\Task\CachingFrameworkGarbageCollectionAdditionalFieldProvider::class
);
The registration is made in the array
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']
.
The key used corresponds to the task class. Then come 4 parameters:
- extension : the key of the extension that provides the class. This is used for informational purposes.
- title : the name of the task. May use localized labels.
- description : a text describing the task. It is displayed in the information screen of the BE module. May use localized labels.
- additionalFields : the name of the class that provides the additional fields. Leave empty if task class does not require any such fields.
Working with serialized objects¶
When a task is registered with the Scheduler the corresponding object instance is serialized and stored in the database (see Appendix A for more details). This is not a very common practice. There are advantages but also some pitfalls, so please read this section carefully.
A serialized object may happen to be “out of sync” with its class if the class changes some of its variables or methods. If a variable’s name is changed or if variables are added or removed, the serialized object will not reflect these changes. The same goes if a method is renamed, added or deleted. Problems will also arise if the number or order of arguments used to call a method are changed. In such cases weird errors may appear, which can be very difficult to track. The only solution is to delete the registered task and register it anew.
To minimize such risks it is worth to consider implementing the
business logic in a separate class, so that the task class itself
changes as little as possible. The execute()
should be as
simple as possible. Consider the following:
class MyTask extends \TYPO3\CMS\Scheduler\Task\AbstractTask {
public function execute() {
$businessLogic = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\Vendor\Extension\BusinessLogic::class);
$businessLogic->run(arg1, arg2, …);
}
}
In such a setup the execute()
is kept to the strict minimum
and the operations themselves are handled by a separate class.
Also remember that the constructor is not called when
unserializing an object. If some operations need to be run upon
unserialization, implement a __wakeup()
method instead.
Saving a task’s state¶
The task’s state is saved automatically at the start of its
execution. If you need to save a task’s state at some point during
its execution, you can simply call the task’s own save()
method.