You too can become a hero and support this Blog at GitHub Sponsors or Patreon.

More about support

In TYPO3 v10 the swiftmailer component (GitHub) has been replaced (Patch, Feature RST, Breaking RST) with two symfony components: symfony/mailer (GitHub, official component page) and symfony/mime (GitHub, official component page). This means that the API for sending emails has slightly changed and that extension code that sends emails will most likely need an update. In this post we will have a look on what the new components do and how we can use them in our code. Finally, we will have a look at a new feature built on top of it: FluidEmail, which enables streamlined and unified looking system wide emails including all system emails sent by the TYPO3 core itself.

But first things first.

The new API with symfony/mailer

Although this post will focus on the PHP side of the mail API, a notice from the TYPO3 docs regarding configuration mail sending on the server side:

The new component does not handle the regular PHP function mail() anymore. Instead, it is recommended to switch to sendmail or smtp, which can be configured within the TYPO3 Install Tool or the Settings module for System Maintainers under “Presets” => “Mail”.

All existing installations which still configure mail are migrated to sendmail by automatically detecting the sendmail path in the PHP.ini settings, but should be reviewed on update.

With this out of the way, let's have a look on how to send an email in TYPO3 v10:

use Symfony\Component\Mime\Address;
use TYPO3\CMS\Core\Mail\MailMessage;

GeneralUtility::makeInstance(MailMessage::class)
    ->from(new Address('benni@corporate.com', 'Benni'))
    ->to(new Address('paula@corporate.com', 'Paula Paulson'))
    ->replyTo('slack@corporate.com')
    ->cc(new Address('paul@corporate.com', 'Paul from Accounting'))
    ->subject('Check Slack ASAP')
    ->text('Hi Paula. I wrote you a slack message.')
    ->html('<h4>Hi Paula.</h4><p>I wrote you a slack message.</p>')
    ->send();

As we see, we still instantiate \TYPO3\CMS\Core\Mail\MailMessage for sending an email. However, MailMessage is extending \Symfony\Component\Mime\Email now. A few things from the example are worth mentioning.

  • The addresses for sender, receiver, reply-to etc. are now best handed over as instances of \Symfony\Component\Mime\Address. This class expects an email address and an optional name as constructor arguments. It will validate the given email address against the RFC 2822 standard and throw an exception if the mail address is invalid. An array containing multiple instances of Address can be passed to the before mentioned methods as well.
  • If the email should be a plain text email (Content-Type: text/plain), we just use $email->text($plainTextContent). For HTML mails (Content-Type: text/html), we use $email->html($htmlContent). If both variants are supplied the email will contain both.
  • $mail->send() will instantiate the class \TYPO3\CMS\Core\Mail\Mailer and send itself. In special cases where it is e.g. necessary to provide a different transport configuration the Mailer could also be instantiated with an implementation of  \Symfony\Component\Mailer\Transport\TransportInterface:
use TYPO3\CMS\Core\Mail\Mailer;

// Retrieve $eventDispatcher preferably through dependency injection
// Available after composer require symfony/google-mailer
$transport = new GmailSmtpTransport('user', 'password');
$mailer = GeneralUtility::makeInstance(Mailer::class, $transport, $eventDispatcher)
$mailer->send($email);

Note that the Mailer expects an instance of the EventDispatcherInterface (read more about Events over at PSR-14 Events in TYPO3) as a second argument, which the above code does provide and thus the AfterMailerInitializationEvent will be emitted. If we do not rely on the Event being fired we could omit passing the dispatcher.

Top

Custom Mailer Registration (TYPO3 v12)

12 LTS

Since TYPO3 12.1 it is possible to register a custom Mailer implementation by simply implementing the interface \TYPO3\CMS\Core\Mail\MailerInterface, which extends \Symfony\Component\Mailer\MailerInterface (Patch, Feature RST).

The core class \TYPO3\CMS\Core\Mail\Mailer is registered as default implementation.

After implementing our custom mailer, we have to add the following lines into the Configuration/Services.yaml file to ensure that our custom mailer is used when we inject the Interface:

TYPO3\CMS\Core\Mail\MailerInterface:
    alias: Vendor\MyExt\Mail\MyMailer

With this configuration, we will receive our implementation whenever we inject the MailerInterface (read more about Dependency Injection in TYPO3).

Top

Email Validators (TYPO3 v11)

11 LTS

With TYPO3 v11 the email validation has been improved (Patch, Feature RST). It is now possible to change the behavior of GeneralUtility::validEmail().

By default, GeneralUtility::validEmail() will use the validator \Egulias\EmailValidator\Validation\RFCValidation.

The following validators are also available:

  • \Egulias\EmailValidator\Validation\DNSCheckValidation

  • \Egulias\EmailValidator\Validation\SpoofCheckValidation

  • \Egulias\EmailValidator\Validation\NoRFCWarningsValidation

We can provide custom implementations of the interface \Egulias\EmailValidator\Validation\EmailValidation.

The validators to use are configured in $GLOBALS['TYPO3_CONF_VARS'] as follows:

$GLOBALS['TYPO3_CONF_VARS']['MAIL']['validators'] = [
  \Egulias\EmailValidator\Validation\RFCValidation::class,
  \Vendor\MyExt\EmailValidation\MyVustomValidation::class
];

If multiple validators are provided, each validator must return true.

Top

MailMessage related PSR-14 Events (TYPO3 v12)

12 LTS

One PSR-14 Event (read more about PSR-14 Events in TYPO3) has always been available: The AfterMailerInitializationEvent allows us to set custom mailing settings.

With TYPO3 12.0 two more Events have been introduced to offer even more flexibility with sending emails (Patch, Feature RST). The BeforeMailerSentMessageEvent is emitted before the mail is actually sent to allow manipulation for e.g. streamlining mails or other preprocessing. The other new event is emitted after the mail has been sent: The AfterMailerSentMessageEvent allows for further processing after (un)successful mail sending.

Note that these events are emitted from the \TYPO3\CMS\Core\Mail\Mailer class. In other words: if we registered a custom implementation like discussed above, we would have to emit the events ourself.

In addition to the before mentioned way to send emails, there is another way available which we will have a look at next: FluidEmail.

Top

FluidEmail - streamline your Emails

With TYPO3 10.3 it has become a lot easier to send emails based on a fluid template. More than that, all emails can now easily share a design and layout including the emails sent by TYPO3s system extensions. The underlying feature is the introduction of the FluidEmail class (Patch, Feature RST).  FluidEmail extends \Symfony\Component\Mime\Email similar to MailMessage as we have seen above.

Emails send with FluidEmail will always use layouts, partials and templates located at the paths configured in

$GLOBALS['TYPO3_CONF_VARS']['MAIL']['layoutRootPaths']
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['partialRootPaths']
$GLOBALS['TYPO3_CONF_VARS']['MAIL']['templateRootPaths']

The TYPO3 core comes with a predefined layout at EXT:core/Resources/Private/Layouts/SystemEmail.html that is used by system emails by default. If we supply a custom LayoutPath to our site package we could change the appearance of all system emails at once and use the new layout and style for our custom emails as well. Let's have a look on how to send a FluidEmail:

use TYPO3\CMS\Core\Mail\FluidEmail;
use TYPO3\CMS\Core\Mail\Mailer;

$email = GeneralUtility::makeInstance(FluidEmail::class)
    ->to($emailAddress)
    ->setTemplate('Confirmation/RegistrationSuccessful')
    ->assign('user', $user);
if ($GLOBALS['TYPO3_REQUEST'] instanceof ServerRequestInterface) {
    $email->setRequest($GLOBALS['TYPO3_REQUEST']);
}
GeneralUtility::makeInstance(Mailer::class)->send($email);

By default, FluidEmail will send a plaintext and a HTML version of the email. This means that we need to provide two fluid templates. In the above example there needs to be a RegistrationSuccessful.html for the HTML content and a RegistrationSuccessful.txt for the plaintext variant. If we want to send an HTML email only, we can do that by setting $email->format(FluidEmail::FORMAT_HTML).

The subject of the email can be provided via $email->subject('subject') or by adding the section "Subject" to the fluid template:

<f:layout name="SystemEmail" />
<f:section name="Title">REGISTRATION SUCCESSFUL</f:section>
<f:section name="Subject">Your registration was successfull</f:section>
<f:section name="Main">
    <h4>Hey there {user.fullName},</h4>
    <p>you have successfully registered for our service. Thank you for your data and welcome on board!</p>
</f:section>

With the default layout shipped by the TYPO3 core the email could look like this. The top half color will depend on the color configured for the backend login in ext:backend. The logo is taken from this configuration as well, if no logo is provided the default is taken as shown in the following screenshot.

Some variables are available by default in the templates of a FluidEmail:

  • {typo3.sitename}
    contains the sitename as defined in $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename']
  • {typo3.formats.date}
    contains the configured date format from $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy']
  • {typo3.formats.time}
    contains the configured time format from $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm']
  • {typo3.systemConfiguration}
    contains the extension configuration array $GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']
  • {typo3.information}
    contains an instance of \TYPO3\CMS\Core\Information\Typo3Information

Additional variables can be assigned to the template as well, as we did in the example above.

If the current Request object is needed in the template FluidEmail offers a helper method setRequest($request) that assures the NormalizedParams are set properly.


With all of this said it should be easy to update existing code to the new email API and integrate custom layouts for a system wide unified email appearance.

Have fun - and send me an email if information provided in this post is missing, misleading or wrong. Thanks for the support on Patreon and Github Sponsors. It's much appreciated. <3

Top

Further Information