SymfonyMailer.php
Same filename in other branches
Namespace
Drupal\Core\Mail\Plugin\MailFile
-
core/
lib/ Drupal/ Core/ Mail/ Plugin/ Mail/ SymfonyMailer.php
View source
<?php
namespace Drupal\Core\Mail\Plugin\Mail;
use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Mail\Attribute\Mail;
use Drupal\Core\Mail\MailFormatHelper;
use Drupal\Core\Mail\MailInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Utility\Error;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Mailer\Mailer;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mailer\Transport;
use Symfony\Component\Mailer\Transport\Dsn;
use Symfony\Component\Mime\Email;
/**
* Defines an experimental mail backend, based on the Symfony mailer component.
*
* This mail plugin acts as a drop-in replacement for the current default PHP
* mail plugin. Mail delivery is based on the Symfony mailer component. Hence,
* all transports registered by default in the Symfony mailer transport factory
* are available via configurable DSN.
*
* By default, this plugin uses `sendmail://default` as the transport DSN. I.e.,
* it attempts to use `/usr/sbin/sendmail -bs` in order to submit a message to
* the MTA. Sites hosted on operating systems without a working MTA (e.g.,
* Windows) need to configure a suitable DSN.
*
* The DSN can be set via the `mailer_dsn` key of the `system.mailer` config.
*
* The following example shows how to switch the default mail plugin to the
* experimental Symfony mailer plugin with a custom DSN using config overrides
* in `settings.php`:
*
* @code
* $config['system.mail']['interface'] = [ 'default' => 'symfony_mailer' ];
* $config['system.mail']['mailer_dsn'] = [
* 'scheme' => 'smtp',
* 'host' => 'smtp.example.com',
* 'port' => 25,
* 'user' => 'user',
* 'password' => 'pass',
* 'options' => [],
* ];
* @endcode
*
* @see https://symfony.com/doc/current/mailer.html#using-built-in-transports
*
* @internal
*/
class SymfonyMailer implements MailInterface, ContainerFactoryPluginInterface {
/**
* A list of headers that can contain multiple email addresses.
*
* @see \Symfony\Component\Mime\Header\Headers::HEADER_CLASS_MAP
*/
protected const MAILBOX_LIST_HEADERS = [
'from',
'to',
'reply-to',
'cc',
'bcc',
];
/**
* List of headers to skip copying from the message array.
*
* Symfony mailer sets Content-Type and Content-Transfer-Encoding according to
* the actual body content. Note that format=flowed is not supported by
* Symfony.
*
* @see \Symfony\Component\Mime\Part\TextPart
*/
protected const SKIP_HEADERS = [
'content-type',
'content-transfer-encoding',
];
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($container->get('logger.channel.mail'));
}
/**
* Symfony mailer constructor.
*
* @param \Psr\Log\LoggerInterface $logger
* The logger service.
* @param \Symfony\Component\Mailer\MailerInterface $mailer
* The mailer service. Only specify an instance in unit tests, pass NULL in
* production.
*/
public function __construct(LoggerInterface $logger, ?MailerInterface $mailer = NULL) {
}
public function format(array $message) {
foreach ($message['body'] as &$part) {
// If the message contains HTML, convert it to plain text (which also
// wraps the mail body).
if ($part instanceof MarkupInterface) {
$part = MailFormatHelper::htmlToText($part);
}
else {
$part = MailFormatHelper::wrapMail($part);
}
}
// Join the body array into one string.
$message['body'] = implode("\n\n", $message['body']);
return $message;
}
public function mail(array $message) {
try {
$email = new Email();
$headers = $email->getHeaders();
foreach ($message['headers'] as $name => $value) {
if (!in_array(strtolower($name), self::SKIP_HEADERS, TRUE)) {
if (in_array(strtolower($name), self::MAILBOX_LIST_HEADERS, TRUE)) {
// Split values by comma, but ignore commas encapsulated in double
// quotes.
$value = str_getcsv($value, ',');
}
$headers->addHeader($name, $value);
}
}
$email->to($message['to'])
->subject($message['subject'])
->text($message['body']);
$mailer = $this->getMailer();
$mailer->send($email);
return TRUE;
} catch (\Exception $e) {
Error::logException($this->logger, $e);
return FALSE;
}
}
/**
* Returns a minimalistic Symfony mailer service.
*/
protected function getMailer() : MailerInterface {
if (!isset($this->mailer)) {
$dsn = \Drupal::config('system.mail')->get('mailer_dsn');
$dsnObject = new Dsn(...$dsn);
// Symfony Mailer and Transport classes both optionally depend on the
// event dispatcher. When provided, a MessageEvent is fired whenever an
// email is prepared before sending.
//
// The MessageEvent will likely play an important role in an upcoming mail
// API. However, emails handled by this plugin already were processed by
// hook_mail and hook_mail_alter. Firing the MessageEvent would leak those
// mails into the code path (i.e., event subscribers) of the new API.
// Therefore, this plugin deliberately refrains from injecting the event
// dispatcher.
$factories = Transport::getDefaultFactories(logger: $this->logger);
$transportFactory = new Transport($factories);
$transport = $transportFactory->fromDsnObject($dsnObject);
$this->mailer = new Mailer($transport);
}
return $this->mailer;
}
}
Classes
Title | Deprecated | Summary |
---|---|---|
SymfonyMailer | Defines an experimental mail backend, based on the Symfony mailer component. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.