class PhpMailerSmtp in PHPMailer SMTP 8
Same name and namespace in other branches
- 2.x src/Plugin/Mail/PhpMailerSmtp.php \Drupal\phpmailer_smtp\Plugin\Mail\PhpMailerSmtp
- 2.0.x src/Plugin/Mail/PhpMailerSmtp.php \Drupal\phpmailer_smtp\Plugin\Mail\PhpMailerSmtp
- 2.1.x src/Plugin/Mail/PhpMailerSmtp.php \Drupal\phpmailer_smtp\Plugin\Mail\PhpMailerSmtp
Implements the base PHPMailer SMTP class for the Drupal MailInterface.
Base PHPMailer for Drupal implementation with support for SMTP keep-alive.
Plugin annotation
@Mail(
id = "phpmailer_smtp",
label = @Translation("PHPMailer SMTP"),
description = @Translation("Sends emails via SMTP using the PHPMailer library.")
)
Hierarchy
- class \Drupal\phpmailer_smtp\Plugin\Mail\PhpMailerSmtp extends \PHPMailer\PHPMailer\PHPMailer implements MailInterface, ContainerFactoryPluginInterface uses StringTranslationTrait
Expanded class hierarchy of PhpMailerSmtp
File
- src/
Plugin/ Mail/ PhpMailerSmtp.php, line 29
Namespace
Drupal\phpmailer_smtp\Plugin\MailView source
class PhpMailerSmtp extends PHPMailer implements MailInterface, ContainerFactoryPluginInterface {
use StringTranslationTrait;
/**
* Config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* PHPMailer SMTP Config.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/**
* Whether to allow sending messages with an empty body.
*
* @var bool
*/
public $AllowEmpty = TRUE;
/**
* Verbose debug output level configured for Drupal.
*
* @var int
*
* In order to be able to log error messages with actual error information and
* see what actually went wrong for a particular message, PHPMailer::SMTPDebug
* always needs to be enabled.
*
* PhpMailerSmtp::SmtpSend() overrides PHPMailer::SmtpSend() to capture the
* debug output string and make it available for \Drupal::logger() calls.
*
* @see PHPMailer::SMTPDebug
* @see SMTP::do_debug
* @see PhpMailerSmtp::SmtpSend()
* @see PhpMailerSmtp::drupalDebugOutput
*/
public $drupalDebug = 0;
/**
* Overrides PHPMailer::SMTPDebug.
*
* @var int
*
* Capture SMTP communication errors by default.
*/
public $SMTPDebug = 2;
/**
* Stores the verbose debug output of the SMTP communication.
*
* @var string
*/
public $drupalDebugOutput = '';
/**
* Logger channel factory.
*
* @var \Drupal\Core\Logger\LoggerChannelFactoryInterface
*/
protected $loggerFactory;
/**
* The messenger.
*
* @var \Drupal\Core\Messenger\MessengerInterface
*/
protected $messenger;
/**
* Creates an instance of the plugin.
*
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* The container to pull out services used in the plugin.
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
*
* @return static
* Returns an instance of this plugin.
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static($container
->get('config.factory'), $container
->get('logger.factory'), $container
->get('messenger'));
}
/**
* Constructor.
*/
public function __construct(ConfigFactoryInterface $config, LoggerChannelFactoryInterface $logger_factory, MessengerInterface $messenger) {
// Throw exceptions instead of dying (since 5.0.0).
if (method_exists(get_parent_class($this), '__construct')) {
parent::__construct(TRUE);
}
$this->configFactory = $config;
$this->config = $config
->get('phpmailer_smtp.settings');
$this->loggerFactory = $logger_factory;
$this->messenger = $messenger;
$this
->IsSMTP();
$this
->Reset();
$this->SMTPAutoTLS = FALSE;
$this->Host = $this->config
->get('smtp_host', '');
if ($backup = $this->config
->get('smtp_hostbackup', '')) {
$this->Host .= ';' . $backup;
}
$this->Port = $this->config
->get('smtp_port', '25');
$smtp_protocol = $this->config
->get('smtp_protocol', '');
$this->SMTPSecure = $smtp_protocol;
if (!empty($smtp_protocol)) {
$ssl_verify_peer = $this->config
->get('smtp_ssl_verify_peer');
$this->SMTPOptions['ssl']['verify_peer'] = isset($ssl_verify_peer) ? $ssl_verify_peer : 1;
$ssl_verify_peer_name = $this->config
->get('smtp_ssl_verify_peer_name');
$this->SMTPOptions['ssl']['verify_peer_name'] = isset($ssl_verify_peer_name) ? $ssl_verify_peer_name : 1;
$ssl_allow_self_signed = $this->config
->get('smtp_ssl_allow_self_signed');
$this->SMTPOptions['ssl']['allow_self_signed'] = isset($ssl_allow_self_signed) ? $ssl_allow_self_signed : 0;
}
// Use SMTP authentication if both username and password are given.
$this->Username = $this->config
->get('smtp_username', '');
$this->Password = $this->config
->get('smtp_password', '');
$this->SMTPAuth = (bool) ($this->Username != '' && $this->Password != '');
$this->SMTPKeepAlive = $this->config
->get('smtp_keepalive', 0);
$this->Timeout = $this->config
->get('smtp_timeout', 30);
$this->drupalDebug = $this->config
->get('smtp_debug', 0);
if ($this->drupalDebug > $this->SMTPDebug && \Drupal::currentUser()
->hasPermission('administer phpmailer smtp settings')) {
$this->SMTPDebug = $this->drupalDebug;
}
}
/**
* Send mail via SMTP.
*
* Wrapper around PHPMailer::SmtpSend() with exception handling.
*/
public function smtpSend($header, $body) {
if ($this->SMTPDebug) {
// Clear possibly previously captured debug output.
$this->drupalDebugOutput = '';
ob_start();
}
try {
$result = parent::SmtpSend($header, $body);
// Close connection when not using SMTP keep-alive.
if (!$this->SMTPKeepAlive) {
$this
->SmtpClose();
}
} catch (Exception $exception) {
}
if ($this->SMTPDebug) {
if ($this->drupalDebug && ($this->drupalDebugOutput = ob_get_contents())) {
$this->messenger
->addMessage(Markup::create($this->drupalDebugOutput));
if ($this->config
->get('smtp_debug_log', 0)) {
$this->loggerFactory
->get('phpmailer_smtp')
->debug('Output of communication with SMTP server:<br /><pre>{output}</pre>', [
'output' => str_replace("<br>\n", "\n", Html::decodeEntities($this->drupalDebugOutput)),
]);
}
}
ob_end_clean();
}
// Reinitialize properties.
$this
->reset();
if (isset($exception)) {
// Pass exception to caller.
throw $exception;
}
return $result;
}
/**
* Re-initialize properties after sending mail.
*/
public function reset() {
$this
->clearAllRecipients();
$this
->clearReplyTos();
$this
->clearAttachments();
$this
->clearCustomHeaders();
$this->Priority = 3;
$this->CharSet = 'utf-8';
$this->ContentType = 'text/plain';
$this->Encoding = '8bit';
// Set default From name.
$from_name = $this->config
->get('smtp_fromname');
if ($from_name == '') {
// Fall back on the site name.
$from_name = $this->configFactory
->get('system.site')
->get('name');
}
$this->FromName = $from_name;
$this->Sender = '';
$this->MessageID = '';
}
/**
* Overrides PHPMailer::__destruct().
*/
public function __destruct() {
// Disable debug output if SMTP keep-alive is enabled.
// PHP is most likely shutting down altogether (this class is instantiated
// as a static singleton). Since logging facilities (e.g., database
// connection) quite potentially have been shut down already, simply turn
// off SMTP debugging. Without this override, debug output would be printed
// on the screen and CLI output.
if ($this->SMTPKeepAlive && isset($this->smtp->do_debug)) {
$this->smtp->do_debug = 0;
}
parent::__destruct();
}
/**
* Provide more user-friendly error messages.
*
* Note: messages should not end with a dot.
*/
public function setLanguage($langcode = 'en', $lang_path = 'language/') {
// Retrieve English defaults to ensure all message keys are set.
parent::SetLanguage('en');
// Overload with Drupal translations.
$this->language = [
'authenticate' => $this
->t('SMTP error: Could not authenticate.'),
'connect_host' => $this
->t('SMTP error: Could not connect to host.'),
'data_not_accepted' => $this
->t('SMTP error: Data not accepted.'),
'smtp_connect_failed' => $this
->t('SMTP error: Could not connect to SMTP host.'),
'smtp_error' => $this
->t('SMTP server error:'),
// Messages used during email generation.
'empty_message' => $this
->t('Message body empty'),
'encoding' => $this
->t('Unknown encoding:'),
'variable_set' => $this
->t('Cannot set or reset variable:'),
'file_access' => $this
->t('File error: Could not access file:'),
'file_open' => $this
->t('File error: Could not open file:'),
// Non-administrative messages.
'from_failed' => $this
->t('The following From address failed:'),
'invalid_address' => $this
->t('Invalid address'),
'provide_address' => $this
->t('You must provide at least one recipient e-mail address.'),
'recipients_failed' => $this
->t('The following recipients failed:'),
] + $this->language;
return TRUE;
}
/**
* Wrap PHPMailer::RFCDate() to return the current timezone.
*/
public static function rfcDate() {
$tz = date('Z');
$tzs = $tz < 0 ? '-' : '+';
$tz = abs($tz);
$tz = (int) ($tz / 3600) * 100 + $tz % 3600 / 60;
$result = sprintf("%s %s%04d", date('D, j M Y H:i:s'), $tzs, $tz);
return $result;
}
/**
* Concatenates and wraps the e-mail body for plain-text mails.
*
* @param array $message
* A message array, as described in hook_mail_alter().
*
* @return array
* The formatted $message.
*/
public function format(array $message) {
// Join the body array into one string.
$message['body'] = implode("\n\n", $message['body']);
// Convert any HTML to plain-text.
$message['body'] = MailFormatHelper::htmlToText($message['body']);
// Wrap the mail body for sending.
$message['body'] = MailFormatHelper::wrapMail($message['body']);
return $message;
}
/**
* Sends an e-mail message composed by drupal_mail().
*
* @param array $message
* A message array, as described in hook_mail_alter().
*
* @return bool
* TRUE if the mail was successfully accepted, otherwise FALSE.
*
* @see PHPMailer::Send()
*/
public function mail(array $message) {
try {
// Parse 'From' address.
$from = phpmailer_smtp_parse_address($message['headers']['From']);
$from = reset($from);
$this->From = $from['mail'];
if ($from['name'] != '') {
$this->FromName = $from['name'];
}
unset($message['headers']['From']);
// @todo This \still\ might not be the correct way to do this.
$phpmailer_smtp_debug_email = $this->configFactory
->get('system.maintenance')
->get('phpmailer_smtp_debug_email');
if (empty($phpmailer_smtp_debug_email)) {
// Set recipients.
foreach (phpmailer_smtp_parse_address($message['to']) as $address) {
$this
->AddAddress($address['mail'], $address['name']);
}
// Extract CCs and BCCs from headers.
if (!empty($message['headers']['Cc'])) {
foreach (phpmailer_smtp_parse_address($message['headers']['Cc']) as $address) {
$this
->AddCC($address['mail'], $address['name']);
}
}
if (!empty($message['headers']['Bcc'])) {
foreach (phpmailer_smtp_parse_address($message['headers']['Bcc']) as $address) {
$this
->AddBCC($address['mail'], $address['name']);
}
}
}
else {
// Reroute to debug e-mail address.
// @todo This might not be the correct way to do this.
$this
->AddAddress($phpmailer_smtp_debug_email);
}
unset($message['headers']['Cc'], $message['headers']['Bcc']);
// Extract Reply-To from headers.
if (isset($message['headers']['Reply-To'])) {
foreach (phpmailer_smtp_parse_address($message['headers']['Reply-To']) as $address) {
$this
->AddReplyTo($address['mail'], $address['name']);
}
unset($message['headers']['Reply-To']);
}
elseif ($this->config
->get('smtp_always_replyto')) {
// If no Reply-To header has been explicitly set, use the From address
// to be able to respond to e-mails sent via Google Mail.
$this
->AddReplyTo($from['mail'], $from['name']);
}
// Extract Content-Type and charset.
if (isset($message['headers']['Content-Type'])) {
$content_type = explode(';', $message['headers']['Content-Type']);
$this->ContentType = trim(array_shift($content_type));
foreach ($content_type as $param) {
$param = explode('=', $param, 2);
$key = trim($param[0]);
if ($key == 'charset') {
$this->CharSet = trim($param[1]);
}
else {
$this->ContentType .= '; ' . $key . '=' . trim($param[1]);
}
}
unset($message['headers']['Content-Type']);
}
// Set additional properties.
$properties = [
'X-Priority' => 'Priority',
'Content-Transfer-Encoding' => 'Encoding',
'Message-ID' => 'MessageID',
];
foreach ($properties as $source => $property) {
if (isset($message['headers'][$source])) {
$this->{$property} = $message['headers'][$source];
unset($message['headers'][$source]);
}
}
// Return-Path should not be set by Drupal.
if (isset($message['headers']['Return-Path'])) {
unset($message['headers']['Return-Path']);
}
// X-Mailer is set by PHPMailer which is the mailer.
if (isset($message['headers']['X-Mailer'])) {
unset($message['headers']['X-Mailer']);
}
// Set default sender address.
$envelopeSender = phpmailer_smtp_parse_address($message['from']);
$envelopeSender = reset($envelopeSender);
$this->Sender = $envelopeSender['mail'];
// Check envelope sender option.
$senderOption = $this->config
->get('smtp_envelope_sender_option');
if ($senderOption === 'site_mail') {
$this->Sender = $this->configFactory
->get('system.site')
->get('mail');
}
if ($senderOption === 'from_address') {
$this->Sender = $from['mail'];
}
if ($senderOption === 'other') {
$this->Sender = $this->config
->get('smtp_envelope_sender');
}
if (!empty($this->config
->get('smtp_ehlo_host'))) {
$this->Hostname = $this->config
->get('smtp_ehlo_host');
}
// This one is always set by PHPMailer.
unset($message['headers']['MIME-Version']);
// Add remaining header lines.
// Note: Any header lines MUST already be checked by the caller for
// unwanted newline characters to avoid header injection.
// @see PHPMailer::SecureHeader()
foreach ($message['headers'] as $key => $value) {
$this
->AddCustomHeader($key, $value);
}
$this->Subject = $message['subject'];
$this->Body = $message['body'];
return $this
->Send();
} catch (Exception $e) {
// Log the error including verbose debug information.
// Since DBLog module is the most common case, we use HTML to format the
// message for visual inspection. For sites running with Syslog or other
// logging modules, we put the actual values on separate lines (\n), so
// the surrounding HTML markup doesn't get too disturbing.
// Message is a safe $this->t() string from PhpMailerSmtp::SetLanguage().
$output = $e
->getMessage();
// Attempt to delimit summary from full message.
$output .= " \n";
$arguments = [];
// Append SMTP communication output.
if ($this->drupalDebugOutput) {
// PHPMailer debug output contains HTML linebreaks. PRE is more
// readable.
$output .= '<p><strong>Server response:</strong></p>';
$output .= "<pre>\n@smtp_output\n</pre>";
$arguments += [
'@smtp_output' => str_replace("<br>\n", "\n", Html::decodeEntities($this->drupalDebugOutput)),
];
}
// We need to log the message in order to be able to debug why the server
// responded with an error. The mail body may contain passwords and other
// sensitive information, which should not be logged. Since all kind of
// mails are processed and Drupal provides no way to mark sensible data,
// it is technically impossible prevent logging in all cases.
// Remove $params; they've already been processed and may contain sensible
// data.
unset($message['params']);
// Subject.
$output .= "<p><strong>Subject:</strong> \n@subject\n</p>";
$arguments += [
'@subject' => $message['subject'],
];
unset($message['subject']);
// Body.
$output .= '<p><strong>Body:</strong></p>';
$output .= "<pre>\n@body\n</pre>";
$arguments += [
'@body' => $message['body'],
];
unset($message['body']);
// Rest of $message.
$output .= '<p><strong>Message:</strong></p>';
$output .= "<pre>\n@message\n</pre>";
$arguments += [
'@message' => var_export($message, TRUE),
];
$this->loggerFactory
->get('phpmailer_smtp')
->error($output, $arguments);
return FALSE;
}
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
PhpMailerSmtp:: |
public | property | Whether to allow sending messages with an empty body. | |
PhpMailerSmtp:: |
protected | property | PHPMailer SMTP Config. | |
PhpMailerSmtp:: |
protected | property | Config factory. | |
PhpMailerSmtp:: |
public | property | Verbose debug output level configured for Drupal. | |
PhpMailerSmtp:: |
public | property | Stores the verbose debug output of the SMTP communication. | |
PhpMailerSmtp:: |
protected | property | Logger channel factory. | |
PhpMailerSmtp:: |
protected | property | The messenger. | |
PhpMailerSmtp:: |
public | property | Overrides PHPMailer::SMTPDebug. | |
PhpMailerSmtp:: |
public static | function |
Creates an instance of the plugin. Overrides ContainerFactoryPluginInterface:: |
|
PhpMailerSmtp:: |
public | function |
Concatenates and wraps the e-mail body for plain-text mails. Overrides MailInterface:: |
|
PhpMailerSmtp:: |
public | function |
Sends an e-mail message composed by drupal_mail(). Overrides MailInterface:: |
|
PhpMailerSmtp:: |
public | function | Re-initialize properties after sending mail. | |
PhpMailerSmtp:: |
public static | function | Wrap PHPMailer::RFCDate() to return the current timezone. | |
PhpMailerSmtp:: |
public | function | Provide more user-friendly error messages. | |
PhpMailerSmtp:: |
public | function | Send mail via SMTP. | |
PhpMailerSmtp:: |
public | function | Constructor. | |
PhpMailerSmtp:: |
public | function | Overrides PHPMailer::__destruct(). | |
StringTranslationTrait:: |
protected | property | The string translation service. | 1 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. |