SMS API
The SMS API allows developers to implement SMS-related features into their plugins.
The subsystem contains an SMS Manager class \core_sms\manager
which facilitates the actions performed by the API.
Some of the actions made possible are:
- Sending messages
- Fetching messages
- Checking the status of a message
- Getting SMS gateways.
Currently, the design of the SMS API features the following plugin types:
Sending an SMS
Messages can be sent using the send()
method of the SMS Manager class, which should be fetched using Dependency Injection, for example:
$message = \core\di::get(\core_sms\manager::class)
->send(
recipientnumber: '+61987654321',
content: 'This is the content of the message',
component: 'mod_example',
messagetype: 'demonstrationmessage',
recipientuserid: $user->id,
issensitive: false,
async: false,
);
A single SMS sent by the API may consist of up to 480 UTF-8 characters. It is up to the message gateway plugin to determine how this message is sent to the recipient.
Any message longer than the maximum length will be immediately rejected.
Parameter consideration while sending messages
When sending a message it's important to add the correct component
(for example tool_mfa
) and messagetype
(for example mfa code
) for record keeping purposes.
One component can have many different types of messages and those types should be clearly mentioned while sending the messages so that they are clear in reporting and other locations.
In future reporting will be available for messages status. See MDL-80963 for further information
Sending messages containing sensitive information
When sending a message containing something like a 2FA login token, you should make use of the issensitive
flag.
Passing this flag prevents the SMS subsystem from storing the content of the message in the message log.
The send()
method return an instance of \core_sms\message
which can be used to check on the message status.
Messages containing sensitive information cannot be sent asynchronously.
Sensitive content is not persisted to the database and is therefore not available in a separate PHP process.
The ability to send messages asynchronously has not yet been implemented. The parameter is included for future compatibility.
See MDL-81015 for more information on this feature.
Fetching messages
Every sent message is stored in the database to support status checks, and for subsequent reporting.
Messages can be fetched from the database by calling the \core_sms\manager::get_message()
and \core_sms\manager::get_messages()
methods and supplying a filter.
$message = \core\di::get(\core_sms\manager::class)
->get_message(['id' => $id]);
$messages = \core\di::get(\core_sms\manager::class)
->get_messages(['recipientuserid' => $userid]);
If the message was sent with the issensitive
flag the message body will not be stored.
Checking the status of a message
Once a message is sent, a status is recorded against it. This can be used to determine whether the message was sent successfully.
The level of status information available will depend on individual message gateways and recipient regions. In some regions delivery status may be available, but not in others.
Message status can be checked using the \core_sms\message::$status
property.
Statuses are represented by a PHP Enum object with each status having a translatable description, and methods to determine whether the message was sent, is failed, or still in-progress.
$message = \core\di::get(\core_sms\manager::class)
->get_message(['id' => $id]);
// Check if the message is failed.
$message->is_failed();
// Check if the message is still in transit.
$message->is_in_progress();
// Check if the message is sent.
$message->is_sent();
// Get the description of the state.
$message->description();
Getting SMS gateways
SMS gateways are plugins that provide a way to interface with external SMS providers. Once a gateway is configured, any component implementing the SMS API can get a list of gateways.
$manager = \core\di::get(\core_sms\manager::class);
$gatewayrecords = $manager->get_gateway_records();
// It is also possible to filter the request.
$gatewayrecords = $manager->get_gateway_records(['id' => $id]);
// To get all the enabled gateway instances.
$gatewayrecords = $manager->get_enabled_gateway_instances();
Important hooks
The SMS API dispatches some hooks which should be considered when implemented by a plugin/component.
- before_gateway_deleted
- before_gateway_disabled
Before deleting or disabling an SMS gateways, these two hooks are dispatched from the SMS API. This allows components that are actively using that gateway to stop the action, or do necessary cleanup. Listening to these hooks is crucial to avoid data loss or accidental deletion when disabling an active gateway.
public static function check_gateway_usage_in_example_plugin(
before_gateway_deleted|before_gateway_disabled $hook,
): void {
try {
$smsgatewayid = (int)get_config('example_plugin', 'smsgateway');
if ($smsgatewayid && $smsgatewayid === (int)$hook->gateway->id) {
$hook->stop_propagation();
}
} catch (\dml_exception $exception) {
$hook->stop_propagation();
}
}