: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if (false === stripos($ini_sendmail_path, 'qmail')) {
$this->Sendmail = '/var/qmail/bin/qmail-inject';
$this->Sendmail = $ini_sendmail_path;
* @param string $address The email address to send to
* @return bool true on success, false if address already used or invalid in some way
public function addAddress($address, $name = '')
return $this->addOrEnqueueAnAddress('to', $address, $name);
* @param string $address The email address to send to
* @return bool true on success, false if address already used or invalid in some way
public function addCC($address, $name = '')
return $this->addOrEnqueueAnAddress('cc', $address, $name);
* @param string $address The email address to send to
* @return bool true on success, false if address already used or invalid in some way
public function addBCC($address, $name = '')
return $this->addOrEnqueueAnAddress('bcc', $address, $name);
* Add a "Reply-To" address.
* @param string $address The email address to reply to
* @return bool true on success, false if address already used or invalid in some way
public function addReplyTo($address, $name = '')
return $this->addOrEnqueueAnAddress('Reply-To', $address, $name);
* Add an address to one of the recipient arrays or to the ReplyTo array. Because PHPMailer
* can't validate addresses with an IDN without knowing the PHPMailer::$CharSet (that can still
* be modified after calling this function), addition of such addresses is delayed until send().
* Addresses that have been added already return false, but do not throw exceptions.
* @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
* @param string $address The email address
* @param string $name An optional username associated with the address
* @return bool true on success, false if address already used or invalid in some way
protected function addOrEnqueueAnAddress($kind, $address, $name)
$address = trim($address);
$pos = strrpos($address, '@');
$error_message = sprintf(
$this->lang('invalid_address'),
$this->setError($error_message);
$this->edebug($error_message);
throw new Exception($error_message);
if ($name !== null && is_string($name)) {
$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
$params = [$kind, $address, $name];
//Enqueue addresses with IDN until we know the PHPMailer::$CharSet.
//Domain is assumed to be whatever is after the last @ symbol in the address
if (static::idnSupported() && $this->has8bitChars(substr($address, ++$pos))) {
if ('Reply-To' !== $kind) {
if (!array_key_exists($address, $this->RecipientsQueue)) {
$this->RecipientsQueue[$address] = $params;
} elseif (!array_key_exists($address, $this->ReplyToQueue)) {
$this->ReplyToQueue[$address] = $params;
//Immediately add standard addresses without IDN.
return call_user_func_array([$this, 'addAnAddress'], $params);
* Set the boundaries to use for delimiting MIME parts.
* If you override this, ensure you set all 3 boundaries to unique values.
* The default boundaries include a "=_" sequence which cannot occur in quoted-printable bodies,
* as suggested by https://www.rfc-editor.org/rfc/rfc2045#section-6.7
public function setBoundaries()
$this->uniqueid = $this->generateId();
$this->boundary[1] = 'b1=_' . $this->uniqueid;
$this->boundary[2] = 'b2=_' . $this->uniqueid;
$this->boundary[3] = 'b3=_' . $this->uniqueid;
* Add an address to one of the recipient arrays or to the ReplyTo array.
* Addresses that have been added already return false, but do not throw exceptions.
* @param string $kind One of 'to', 'cc', 'bcc', or 'ReplyTo'
* @param string $address The email address to send, resp. to reply to
* @return bool true on success, false if address already used or invalid in some way
protected function addAnAddress($kind, $address, $name = '')
if (!in_array($kind, ['to', 'cc', 'bcc', 'Reply-To'])) {
$error_message = sprintf(
$this->lang('Invalid recipient kind'),
$this->setError($error_message);
$this->edebug($error_message);
throw new Exception($error_message);
if (!static::validateAddress($address)) {
$error_message = sprintf(
$this->lang('invalid_address'),
$this->setError($error_message);
$this->edebug($error_message);
throw new Exception($error_message);
if ('Reply-To' !== $kind) {
if (!array_key_exists(strtolower($address), $this->all_recipients)) {
$this->{$kind}[] = [$address, $name];
$this->all_recipients[strtolower($address)] = true;
} elseif (!array_key_exists(strtolower($address), $this->ReplyTo)) {
$this->ReplyTo[strtolower($address)] = [$address, $name];
* Parse and validate a string containing one or more RFC822-style comma-separated email addresses
* of the form "display name <address>" into an array of name/address pairs.
* Uses the imap_rfc822_parse_adrlist function if the IMAP extension is available.
* Note that quotes in the name part are removed.
* @see http://www.andrew.cmu.edu/user/agreen1/testing/mrbs/web/Mail/RFC822.php A more careful implementation
* @param string $addrstr The address list string
* @param bool $useimap Whether to use the IMAP extension to parse the list
* @param string $charset The charset to use when decoding the address list string.
public static function parseAddresses($addrstr, $useimap = true, $charset = self::CHARSET_ISO88591)
if ($useimap && function_exists('imap_rfc822_parse_adrlist')) {
//Use this built-in parser if it's available
$list = imap_rfc822_parse_adrlist($addrstr, '');
// Clear any potential IMAP errors to get rid of notices being thrown at end of script.
foreach ($list as $address) {
'.SYNTAX-ERROR.' !== $address->host &&
static::validateAddress($address->mailbox . '@' . $address->host)
//Decode the name part if it's present and encoded
property_exists($address, 'personal') &&
//Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
defined('MB_CASE_UPPER') &&
preg_match('/^=\?.*\?=$/s', $address->personal)
$origCharset = mb_internal_encoding();
mb_internal_encoding($charset);
//Undo any RFC2047-encoded spaces-as-underscores
$address->personal = str_replace('_', '=20', $address->personal);
$address->personal = mb_decode_mimeheader($address->personal);
mb_internal_encoding($origCharset);
'name' => (property_exists($address, 'personal') ? $address->personal : ''),
'address' => $address->mailbox . '@' . $address->host,
//Use this simpler parser
$list = explode(',', $addrstr);
foreach ($list as $address) {
$address = trim($address);
//Is there a separate name part?
if (strpos($address, '<') === false) {
//No separate name, just use the whole thing
if (static::validateAddress($address)) {
list($name, $email) = explode('<', $address);
$email = trim(str_replace('>', '', $email));
if (static::validateAddress($email)) {
//Check for a Mbstring constant rather than using extension_loaded, which is sometimes disabled
//If this name is encoded, decode it
if (defined('MB_CASE_UPPER') && preg_match('/^=\?.*\?=$/s', $name)) {
$origCharset = mb_internal_encoding();
mb_internal_encoding($charset);
//Undo any RFC2047-encoded spaces-as-underscores
$name = str_replace('_', '=20', $name);
$name = mb_decode_mimeheader($name);
mb_internal_encoding($origCharset);
//Remove any surrounding quotes and spaces from the name
'name' => trim($name, '\'" '),
* Set the From and FromName properties.
* @param bool $auto Whether to also set the Sender address, defaults to true
public function setFrom($address, $name = '', $auto = true)
$address = trim((string)$address);
$name = trim(preg_replace('/[\r\n]+/', '', $name)); //Strip breaks and trim
//Don't validate now addresses with IDN. Will be done in send().
$pos = strrpos($address, '@');
|| ((!$this->has8bitChars(substr($address, ++$pos)) || !static::idnSupported())
&& !static::validateAddress($address))
$error_message = sprintf(
$this->lang('invalid_address'),
$this->setError($error_message);
$this->edebug($error_message);
throw new Exception($error_message);
if ($auto && empty($this->Sender)) {
$this->Sender = $address;
* Return the Message-ID header of the last email.
* Technically this is the value from the last time the headers were created,
* but it's also the message ID of the last sent message except in
public function getLastMessageID()
return $this->lastMessageID;
* Check that a string looks like an email address.
* Validation patterns supported:
* * `auto` Pick best pattern automatically;
* * `pcre8` Use the squiloople.com pattern, requires PCRE > 8.0;
* * `pcre` Use old PCRE implementation;
* * `php` Use PHP built-in FILTER_VALIDATE_EMAIL;
* * `html5` Use the pattern given by the HTML5 spec for 'email' type form input elements.
* * `noregex` Don't use a regex: super fast, really dumb.
* Alternatively you may pass in a callable to inject your own validator, for example:
* PHPMailer::validateAddress('user@example.com', function($address) {
* return (strpos($address, '@') !== false);
* You can also set the PHPMailer::$validator static to a callable, allowing built-in methods to use your validator.
* @param string $address The email address to check
* @param string|callable $patternselect Which pattern to use
public static function validateAddress($address, $patternselect = null)
if (null === $patternselect) {
$patternselect = static::$validator;
//Don't allow strings as callables, see SECURITY.md and CVE-2021-3603
if (is_callable($patternselect) && !is_string($patternselect)) {
return call_user_func($patternselect, $address);
//Reject line breaks in addresses; it's valid RFC5322, but not RFC5321
if (strpos($address, "\n") !== false || strpos($address, "\r") !== false) {
switch ($patternselect) {
case 'pcre': //Kept for BC
* A more complex and more permissive version of the RFC5322 regex on which FILTER_VALIDATE_EMAIL
* In addition to the addresses allowed by filter_var, also permits:
* * dotless domains: `a@b`
* * comments: `1234 @ local(blah) .machine .example`
* * quoted elements: `'"test blah"@example.org'`
* * numeric TLDs: `a@b.123`
* * unbracketed IPv4 literals: `a@192.168.0.1`
* * IPv6 literals: 'first.last@[IPv6:a1::]'
* Not all of these will necessarily work for sending!
* @see http://squiloople.com/2009/12/20/email-address-validation/
* @copyright 2009-2010 Michael Rushton
* Feel free to use and redistribute this code. But please keep this copyright notice.
return (bool) preg_match(
'/^(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){255,})(?!(?>(?1)"?(?>\\\[ -~]|[^"])"?(?1)){65,}@)' .
'((?>(?>(?>((?>(?>(?>\x0D\x0A)?[\t ])+|(?>[\t ]*\x0D\x0A)?[\t ]+)?)(\((?>(?2)' .
'(?>[\x01-\x08\x0B\x0C\x0E-\'*-\[\]-\x7F]|\\\[\x00-\x7F]|(?3)))*(?2)\)))+(?2))|(?2))?)' .
'([!#-\'*+\/-9=?^-~-]+|"(?>(?2)(?>[\x01-\x08\x0B\x0C\x0E-!#-\[\]-\x7F]|\\\[\x00-\x7F]))*' .
'(?2)")(?>(?1)\.(?1)(?4))*(?1)@(?!(?1)[a-z0-9-]{64,})(?1)(?>([a-z0-9](?>[a-z0-9-]*[a-z0-9])?)' .
'(?>(?1)\.(?!(?1)[a-z0-9-]{64,})(?1)(?5)){0,126}|\[(?:(?>IPv6:(?>([a-f0-9]{1,4})(?>:(?6)){7}' .
'|(?!(?:.*[a-f0-9][:\]]){8,})((?6)(?>:(?6)){0,6})?::(?7)?))|(?>(?>IPv6:(?>(?6)(?>:(?6)){5}:' .
'|(?!(?:.*[a-f0-9]:){6,})(?8)?::(?>((?6)(?>:(?6)){0,4}):)?))?(25[0-5]|2[0-4][0-9]|1[0-9]{2}' .
'|[1-9]?[0-9])(?>\.(?9)){3}))\])(?1)$/isD',
* This is the pattern used in the HTML5 spec for validation of 'email' type form input elements.
* @see https://html.spec.whatwg.org/#e-mail-state-(type=email)
return (bool) preg_match(
'/^[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}' .
'[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/sD',
return filter_var($address, FILTER_VALIDATE_EMAIL) !== false;
* Tells whether IDNs (Internationalized Domain Names) are supported or not. This requires the
* `intl` and `mbstring` PHP extensions.
* @return bool `true` if required functions for IDN support are present
public static function idnSupported()
return function_exists('idn_to_ascii') && function_exists('mb_convert_encoding');
* Converts IDN in given email address to its ASCII form, also known as punycode, if possible.
* Important: Address must be passed in same encoding as currently set in PHPMailer::$CharSet.
* This function silently returns unmodified address if:
* - No conversion is necessary (i.e. domain name is not an IDN, or is already in ASCII form)
* - Conversion to punycode is impossible (e.g. required PHP functions are not available)
* or fails for any reason (e.g. domain contains characters not allowed in an IDN).
* @see PHPMailer::$CharSet
* @param string $address The email address to convert
* @return string The encoded address in ASCII form
public function punyencodeAddress($address)
//Verify we have required functions, CharSet, and at-sign.
$pos = strrpos($address, '@');
!empty($this->CharSet) &&
$domain = substr($address, ++$pos);
//Verify CharSet string is a valid one, and domain properly encoded in this CharSet.
if ($this->has8bitChars($domain) && @mb_check_encoding($domain, $this->CharSet)) {
//Convert the domain from whatever charset it's in to UTF-8
$domain = mb_convert_encoding($domain, self::CHARSET_UTF8, $this->CharSet);
//Ignore IDE complaints about this line - method signature changed in PHP 5.4
if (defined('INTL_IDNA_VARIANT_UTS46')) {
//Use the current punycode standard (appeared in PHP 7.2)
$punycode = idn_to_ascii(
\IDNA_DEFAULT | \IDNA_USE_STD3_RULES | \IDNA_CHECK_BIDI |
\IDNA_CHECK_CONTEXTJ | \IDNA_NONTRANSITIONAL_TO_ASCII,
} elseif (defined('INTL_IDNA_VARIANT_2003')) {
//Fall back to this old, deprecated/removed encoding
// phpcs:ignore PHPCompatibility.Constants.RemovedConstants.intl_idna_variant_2003Deprecated
$punycode = idn_to_ascii($domain, $errorcode, \INTL_IDNA_VARIANT_2003);
//Fall back to a default we don't know about
// phpcs:ignore PHPCompatibility.ParameterValues.NewIDNVariantDefault.NotSet
$punycode = idn_to_ascii($domain, $errorcode);