: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* Check if this message has an alternative body set.
public function alternativeExists()
return !empty($this->AltBody);
* Clear queued addresses of given kind.
* @param string $kind 'to', 'cc', or 'bcc'
public function clearQueuedAddresses($kind)
$this->RecipientsQueue = array_filter(
static function ($params) use ($kind) {
return $params[0] !== $kind;
* Clear all To recipients.
public function clearAddresses()
foreach ($this->to as $to) {
unset($this->all_recipients[strtolower($to[0])]);
$this->clearQueuedAddresses('to');
* Clear all CC recipients.
public function clearCCs()
foreach ($this->cc as $cc) {
unset($this->all_recipients[strtolower($cc[0])]);
$this->clearQueuedAddresses('cc');
* Clear all BCC recipients.
public function clearBCCs()
foreach ($this->bcc as $bcc) {
unset($this->all_recipients[strtolower($bcc[0])]);
$this->clearQueuedAddresses('bcc');
* Clear all ReplyTo recipients.
public function clearReplyTos()
$this->ReplyToQueue = [];
* Clear all recipient types.
public function clearAllRecipients()
$this->all_recipients = [];
$this->RecipientsQueue = [];
* Clear all filesystem, string, and binary attachments.
public function clearAttachments()
* Clear all custom headers.
public function clearCustomHeaders()
$this->CustomHeader = [];
* Clear a specific custom header by name or name and value.
* $name value can be overloaded to contain
* both header name and value (name:value).
* @param string $name Custom header name
* @param string|null $value Header value
* @return bool True if a header was replaced successfully
public function clearCustomHeader($name, $value = null)
if (null === $value && strpos($name, ':') !== false) {
//Value passed in as name:value
list($name, $value) = explode(':', $name, 2);
$value = (null === $value) ? null : trim($value);
foreach ($this->CustomHeader as $k => $pair) {
// We remove the header if the value is not provided or it matches.
if (null === $value || $pair[1] == $value) {
unset($this->CustomHeader[$k]);
* Replace a custom header.
* $name value can be overloaded to contain
* both header name and value (name:value).
* @param string $name Custom header name
* @param string|null $value Header value
* @return bool True if a header was replaced successfully
public function replaceCustomHeader($name, $value = null)
if (null === $value && strpos($name, ':') !== false) {
//Value passed in as name:value
list($name, $value) = explode(':', $name, 2);
$value = (null === $value) ? '' : trim($value);
foreach ($this->CustomHeader as $k => $pair) {
unset($this->CustomHeader[$k]);
if (strpbrk($name . $value, "\r\n") !== false) {
throw new Exception($this->lang('invalid_header'));
$this->CustomHeader[$k] = [$name, $value];
* Add an error message to the error container.
protected function setError($msg)
if ('smtp' === $this->Mailer && null !== $this->smtp) {
$lasterror = $this->smtp->getError();
if (!empty($lasterror['error'])) {
$msg .= $this->lang('smtp_error') . $lasterror['error'];
if (!empty($lasterror['detail'])) {
$msg .= ' ' . $this->lang('smtp_detail') . $lasterror['detail'];
if (!empty($lasterror['smtp_code'])) {
$msg .= ' ' . $this->lang('smtp_code') . $lasterror['smtp_code'];
if (!empty($lasterror['smtp_code_ex'])) {
$msg .= ' ' . $this->lang('smtp_code_ex') . $lasterror['smtp_code_ex'];
* Return an RFC 822 formatted date.
public static function rfcDate()
//Set the time zone to whatever the default is to avoid 500 errors
//Will default to UTC if it's not set properly in php.ini
date_default_timezone_set(@date_default_timezone_get());
return date('D, j M Y H:i:s O');
* Get the server hostname.
* Returns 'localhost.localdomain' if unknown.
protected function serverHostname()
if (!empty($this->Hostname)) {
$result = $this->Hostname;
} elseif (isset($_SERVER) && array_key_exists('SERVER_NAME', $_SERVER)) {
$result = $_SERVER['SERVER_NAME'];
} elseif (function_exists('gethostname') && gethostname() !== false) {
} elseif (php_uname('n') !== false) {
$result = php_uname('n');
if (!static::isValidHost($result)) {
return 'localhost.localdomain';
* Validate whether a string contains a valid value to use as a hostname or IP address.
* IPv6 addresses must include [], e.g. `[::1]`, not just `::1`.
* @param string $host The host name or IP address to check
public static function isValidHost($host)
|| !preg_match('/^([a-zA-Z\d.-]*|\[[a-fA-F\d:]+\])$/', $host)
//Looks like a bracketed IPv6 address
if (strlen($host) > 2 && substr($host, 0, 1) === '[' && substr($host, -1, 1) === ']') {
return filter_var(substr($host, 1, -1), FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) !== false;
//If removing all the dots results in a numeric string, it must be an IPv4 address.
//Need to check this first because otherwise things like `999.0.0.0` are considered valid host names
if (is_numeric(str_replace('.', '', $host))) {
//Is it a valid IPv4 address?
return filter_var($host, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4) !== false;
//Is it a syntactically valid hostname (when embeded in a URL)?
return filter_var('http://' . $host, FILTER_VALIDATE_URL) !== false;
* Get an error message in the current language.
protected function lang($key)
if (count($this->language) < 1) {
$this->setLanguage(); //Set the default language
if (array_key_exists($key, $this->language)) {
if ('smtp_connect_failed' === $key) {
//Include a link to troubleshooting docs on SMTP connection failure.
//This is by far the biggest cause of support questions
//but it's usually not PHPMailer's fault.
return $this->language[$key] . ' https://github.com/PHPMailer/PHPMailer/wiki/Troubleshooting';
return $this->language[$key];
//Return the key as a fallback
* Build an error message starting with a generic one and adding details if possible.
* @param string $base_key
private function getSmtpErrorMessage($base_key)
$message = $this->lang($base_key);
$error = $this->smtp->getError();
if (!empty($error['error'])) {
$message .= ' ' . $error['error'];
if (!empty($error['detail'])) {
$message .= ' ' . $error['detail'];
* Check if an error occurred.
* @return bool True if an error did occur
public function isError()
return $this->error_count > 0;
* $name value can be overloaded to contain
* both header name and value (name:value).
* @param string $name Custom header name
* @param string|null $value Header value
* @return bool True if a header was set successfully
public function addCustomHeader($name, $value = null)
if (null === $value && strpos($name, ':') !== false) {
//Value passed in as name:value
list($name, $value) = explode(':', $name, 2);
$value = (null === $value) ? '' : trim($value);
//Ensure name is not empty, and that neither name nor value contain line breaks
if (empty($name) || strpbrk($name . $value, "\r\n") !== false) {
throw new Exception($this->lang('invalid_header'));
$this->CustomHeader[] = [$name, $value];
* Returns all custom headers.
public function getCustomHeaders()
return $this->CustomHeader;
* Create a message body from an HTML string.
* Automatically inlines images and creates a plain-text version by converting the HTML,
* overwriting any existing values in Body and AltBody.
* Do not source $message content from user input!
* $basedir is prepended when handling relative URLs, e.g. <img src="/images/a.png"> and must not be empty
* will look for an image file in $basedir/images/a.png and convert it to inline.
* If you don't provide a $basedir, relative paths will be left untouched (and thus probably break in email)
* Converts data-uri images into embedded attachments.
* If you don't want to apply these transformations to your HTML, just set Body and AltBody directly.
* @param string $message HTML message string
* @param string $basedir Absolute path to a base directory to prepend to relative paths to images
* @param bool|callable $advanced Whether to use the internal HTML to text converter
* or your own custom converter
* @return string The transformed message body
* @see PHPMailer::html2text()
public function msgHTML($message, $basedir = '', $advanced = false)
preg_match_all('/(?<!-)(src|background)=["\'](.*)["\']/Ui', $message, $images);
if (array_key_exists(2, $images)) {
if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) {
//Ensure $basedir has a trailing /
foreach ($images[2] as $imgindex => $url) {
//Convert data URIs into embedded images
//e.g. "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
if (preg_match('#^data:(image/(?:jpe?g|gif|png));?(base64)?,(.+)#', $url, $match)) {
if (count($match) === 4 && static::ENCODING_BASE64 === $match[2]) {
$data = base64_decode($match[3]);
} elseif ('' === $match[2]) {
$data = rawurldecode($match[3]);
//Not recognised so leave it alone
//Hash the decoded data, not the URL, so that the same data-URI image used in multiple places
//will only be embedded once, even if it used a different encoding
$cid = substr(hash('sha256', $data), 0, 32) . '@phpmailer.0'; //RFC2392 S 2
if (!$this->cidExists($cid)) {
$this->addStringEmbeddedImage(
$images[1][$imgindex] . '="cid:' . $cid . '"',
//Only process relative URLs if a basedir is provided (i.e. no absolute local paths)
//Ignore URLs containing parent dir traversal (..)
&& (strpos($url, '..') === false)
//Do not change urls that are already inline images
&& 0 !== strpos($url, 'cid:')
//Do not change absolute URLs, including anonymous protocol
&& !preg_match('#^[a-z][a-z0-9+.-]*:?//#i', $url)
$filename = static::mb_pathinfo($url, PATHINFO_BASENAME);
$directory = dirname($url);
if ('.' === $directory) {
$cid = substr(hash('sha256', $url), 0, 32) . '@phpmailer.0';
if (strlen($basedir) > 1 && '/' !== substr($basedir, -1)) {
if (strlen($directory) > 1 && '/' !== substr($directory, -1)) {
$basedir . $directory . $filename,
static::_mime_types((string) static::mb_pathinfo($filename, PATHINFO_EXTENSION))
'/' . $images[1][$imgindex] . '=["\']' . preg_quote($url, '/') . '["\']/Ui',
$images[1][$imgindex] . '="cid:' . $cid . '"',
//Convert all message body line breaks to LE, makes quoted-printable encoding work much better
$this->Body = static::normalizeBreaks($message);
$this->AltBody = static::normalizeBreaks($this->html2text($message, $advanced));
if (!$this->alternativeExists()) {
$this->AltBody = 'This is an HTML-only message. To view it, activate HTML in your email application.'
* Convert an HTML string into plain text.
* This is used by msgHTML().
* Note - older versions of this function used a bundled advanced converter
* which was removed for license reasons in #232.
* //Use default conversion
* $plain = $mail->html2text($html);