: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Provide an instance to use for SMTP operations.
public function setSMTPInstance(SMTP $smtp)
* Provide SMTP XCLIENT attributes
* @param string $name Attribute name
* @param ?string $value Attribute value
public function setSMTPXclientAttribute($name, $value)
if (!in_array($name, SMTP::$xclient_allowed_attributes)) {
if (isset($this->SMTPXClient[$name]) && $value === null) {
unset($this->SMTPXClient[$name]);
} elseif ($value !== null) {
$this->SMTPXClient[$name] = $value;
* Get SMTP XCLIENT attributes
public function getSMTPXclientAttributes()
return $this->SMTPXClient;
* Returns false if there is a bad MAIL FROM, RCPT, or DATA input.
* @see PHPMailer::setSMTPInstance() to use a different class.
* @uses \PHPMailer\PHPMailer\SMTP
* @param string $header The message headers
* @param string $body The message body
protected function smtpSend($header, $body)
$header = static::stripTrailingWSP($header) . static::$LE . static::$LE;
if (!$this->smtpConnect($this->SMTPOptions)) {
throw new Exception($this->lang('smtp_connect_failed'), self::STOP_CRITICAL);
//Sender already validated in preSend()
if ('' === $this->Sender) {
$smtp_from = $this->From;
$smtp_from = $this->Sender;
if (count($this->SMTPXClient)) {
$this->smtp->xclient($this->SMTPXClient);
if (!$this->smtp->mail($smtp_from)) {
$this->setError($this->lang('from_failed') . $smtp_from . ' : ' . implode(',', $this->smtp->getError()));
throw new Exception($this->ErrorInfo, self::STOP_CRITICAL);
//Attempt to send to all recipients
foreach ([$this->to, $this->cc, $this->bcc] as $togroup) {
foreach ($togroup as $to) {
if (!$this->smtp->recipient($to[0], $this->dsn)) {
$error = $this->smtp->getError();
$bad_rcpt[] = ['to' => $to[0], 'error' => $error['detail']];
$callbacks[] = ['issent' => $isSent, 'to' => $to[0], 'name' => $to[1]];
//Only send the DATA command if we have viable recipients
if ((count($this->all_recipients) > count($bad_rcpt)) && !$this->smtp->data($header . $body)) {
throw new Exception($this->lang('data_not_accepted'), self::STOP_CRITICAL);
$smtp_transaction_id = $this->smtp->getLastTransactionID();
if ($this->SMTPKeepAlive) {
foreach ($callbacks as $cb) {
[[$cb['to'], $cb['name']]],
['smtp_transaction_id' => $smtp_transaction_id]
//Create error message for any bad addresses
if (count($bad_rcpt) > 0) {
foreach ($bad_rcpt as $bad) {
$errstr .= $bad['to'] . ': ' . $bad['error'];
throw new Exception($this->lang('recipients_failed') . $errstr, self::STOP_CONTINUE);
* Initiate a connection to an SMTP server.
* Returns false if the operation failed.
* @param array $options An array of options compatible with stream_context_create()
* @uses \PHPMailer\PHPMailer\SMTP
public function smtpConnect($options = null)
if (null === $this->smtp) {
$this->smtp = $this->getSMTPInstance();
//If no options are provided, use whatever is set in the instance
$options = $this->SMTPOptions;
if ($this->smtp->connected()) {
$this->smtp->setTimeout($this->Timeout);
$this->smtp->setDebugLevel($this->SMTPDebug);
$this->smtp->setDebugOutput($this->Debugoutput);
$this->smtp->setVerp($this->do_verp);
if ($this->Host === null) {
$this->Host = 'localhost';
$hosts = explode(';', $this->Host);
foreach ($hosts as $hostentry) {
'/^(?:(ssl|tls):\/\/)?(.+?)(?::(\d+))?$/',
$this->edebug($this->lang('invalid_hostentry') . ' ' . trim($hostentry));
//$hostinfo[1]: optional ssl or tls prefix
//$hostinfo[2]: the hostname
//$hostinfo[3]: optional port number
//The host string prefix can temporarily override the current setting for SMTPSecure
//If it's not specified, the default value is used
//Check the host name is a valid name or IP address before trying to use it
if (!static::isValidHost($hostinfo[2])) {
$this->edebug($this->lang('invalid_host') . ' ' . $hostinfo[2]);
$secure = $this->SMTPSecure;
$tls = (static::ENCRYPTION_STARTTLS === $this->SMTPSecure);
if ('ssl' === $hostinfo[1] || ('' === $hostinfo[1] && static::ENCRYPTION_SMTPS === $this->SMTPSecure)) {
$tls = false; //Can't have SSL and TLS at the same time
$secure = static::ENCRYPTION_SMTPS;
} elseif ('tls' === $hostinfo[1]) {
//TLS doesn't use a prefix
$secure = static::ENCRYPTION_STARTTLS;
//Do we need the OpenSSL extension?
$sslext = defined('OPENSSL_ALGO_SHA256');
if (static::ENCRYPTION_STARTTLS === $secure || static::ENCRYPTION_SMTPS === $secure) {
//Check for an OpenSSL constant rather than using extension_loaded, which is sometimes disabled
throw new Exception($this->lang('extension_missing') . 'openssl', self::STOP_CRITICAL);
array_key_exists(3, $hostinfo) &&
is_numeric($hostinfo[3]) &&
$port = (int) $hostinfo[3];
if ($this->smtp->connect($prefix . $host, $port, $this->Timeout, $options)) {
$hello = $this->serverHostname();
$this->smtp->hello($hello);
//Automatically enable TLS encryption if:
//* we are not connecting to localhost
//* we have openssl extension
//* we are not already using SSL
//* the server offers STARTTLS
$this->Host !== 'localhost' &&
$this->smtp->getServerExt('STARTTLS')
if (!$this->smtp->startTLS()) {
$message = $this->getSmtpErrorMessage('connect_host');
throw new Exception($message);
//We must resend EHLO after TLS negotiation
$this->smtp->hello($hello);
$this->SMTPAuth && !$this->smtp->authenticate(
throw new Exception($this->lang('authenticate'));
} catch (Exception $exc) {
$this->edebug($exc->getMessage());
//We must have connected, but then failed TLS or Auth, so close connection nicely
//If we get here, all connection attempts have failed, so close connection hard
//As we've caught all exceptions, just report whatever the last one was
if ($this->exceptions && null !== $lastexception) {
// no exception was thrown, likely $this->smtp->connect() failed
$message = $this->getSmtpErrorMessage('connect_host');
throw new Exception($message);
* Close the active SMTP session if one exists.
public function smtpClose()
if ((null !== $this->smtp) && $this->smtp->connected()) {
* Set the language for error messages.
* The default language is English.
* @param string $langcode ISO 639-1 2-character language code (e.g. French is "fr")
* Optionally, the language code can be enhanced with a 4-character
* script annotation and/or a 2-character country annotation.
* @param string $lang_path Path to the language file directory, with trailing separator (slash)
* Do not set this from user input!
* @return bool Returns true if the requested language was loaded, false otherwise.
public function setLanguage($langcode = 'en', $lang_path = '')
//Backwards compatibility for renamed language codes
if (array_key_exists($langcode, $renamed_langcodes)) {
$langcode = $renamed_langcodes[$langcode];
//Define full set of translatable strings in English
'authenticate' => 'SMTP Error: Could not authenticate.',
'buggy_php' => 'Your version of PHP is affected by a bug that may result in corrupted messages.' .
' To fix it, switch to sending using SMTP, disable the mail.add_x_header option in' .
' your php.ini, switch to MacOS or Linux, or upgrade your PHP to version 7.0.17+ or 7.1.3+.',
'connect_host' => 'SMTP Error: Could not connect to SMTP host.',
'data_not_accepted' => 'SMTP Error: data not accepted.',
'empty_message' => 'Message body empty',
'encoding' => 'Unknown encoding: ',
'execute' => 'Could not execute: ',
'extension_missing' => 'Extension missing: ',
'file_access' => 'Could not access file: ',
'file_open' => 'File Error: Could not open file: ',
'from_failed' => 'The following From address failed: ',
'instantiate' => 'Could not instantiate mail function.',
'invalid_address' => 'Invalid address: ',
'invalid_header' => 'Invalid header name or value',
'invalid_hostentry' => 'Invalid hostentry: ',
'invalid_host' => 'Invalid host: ',
'mailer_not_supported' => ' mailer is not supported.',
'provide_address' => 'You must provide at least one recipient email address.',
'recipients_failed' => 'SMTP Error: The following recipients failed: ',
'signing' => 'Signing Error: ',
'smtp_code' => 'SMTP code: ',
'smtp_code_ex' => 'Additional SMTP info: ',
'smtp_connect_failed' => 'SMTP connect() failed.',
'smtp_detail' => 'Detail: ',
'smtp_error' => 'SMTP server error: ',
'variable_set' => 'Cannot set or reset variable: ',
//Calculate an absolute path so it can work if CWD is not here
$lang_path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'language' . DIRECTORY_SEPARATOR;
$langcode = strtolower($langcode);
!preg_match('/^(?P<lang>[a-z]{2})(?P<script>_[a-z]{4})?(?P<country>_[a-z]{2})?$/', $langcode, $matches)
//There is no English translation file
if ('en' !== $langcode) {
if (!empty($matches['script']) && !empty($matches['country'])) {
$langcodes[] = $matches['lang'] . $matches['script'] . $matches['country'];
if (!empty($matches['country'])) {
$langcodes[] = $matches['lang'] . $matches['country'];
if (!empty($matches['script'])) {
$langcodes[] = $matches['lang'] . $matches['script'];
$langcodes[] = $matches['lang'];
//Try and find a readable language file for the requested language.
foreach ($langcodes as $code) {
$lang_file = $lang_path . 'phpmailer.lang-' . $code . '.php';
if (static::fileIsAccessible($lang_file)) {
if ($foundFile === false) {
$lines = file($lang_file);
foreach ($lines as $line) {
//Translation file lines look like this:
//$PHPMAILER_LANG['authenticate'] = 'SMTP-Fehler: Authentifizierung fehlgeschlagen.';
//These files are parsed as text and not PHP so as to avoid the possibility of code injection
//See https://blog.stevenlevithan.com/archives/match-quoted-string
'/^\$PHPMAILER_LANG\[\'([a-z\d_]+)\'\]\s*=\s*(["\'])(.+)*?\2;/',
//Ignore unknown translation keys
array_key_exists($matches[1], $PHPMAILER_LANG)
//Overwrite language-specific strings so we'll never have missing translation keys.
$PHPMAILER_LANG[$matches[1]] = (string)$matches[3];
$this->language = $PHPMAILER_LANG;
return $foundlang; //Returns false if language not found
* Get the array of strings for the current language.
public function getTranslations()
if (empty($this->language)) {
$this->setLanguage(); // Set the default language.
* Create recipient headers.
* @param array $addr An array of recipients,
* where each recipient is a 2-element indexed array with element 0 containing an address
* and element 1 containing a name, like:
* [['joe@example.com', 'Joe User'], ['zoe@example.com', 'Zoe User']]
public function addrAppend($type, $addr)
foreach ($addr as $address) {
$addresses[] = $this->addrFormat($address);
return $type . ': ' . implode(', ', $addresses) . static::$LE;
* Format an address for use in a message header.
* @param array $addr A 2-element indexed array, element 0 containing an address, element 1 containing a name like
* ['joe@example.com', 'Joe User']
public function addrFormat($addr)
if (!isset($addr[1]) || ($addr[1] === '')) { //No name provided
return $this->secureHeader($addr[0]);
return $this->encodeHeader($this->secureHeader($addr[1]), 'phrase') .
' <' . $this->secureHeader($addr[0]) . '>';
* For use with mailers that do not automatically perform wrapping
* and for quoted-printable encoded messages.
* Original written by philippe.
* @param string $message The message to wrap
* @param int $length The line length to wrap to
* @param bool $qp_mode Whether to run in Quoted-Printable mode