: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$this->setError($this->lang('encoding') . $encoding);
throw new Exception($this->lang('encoding') . $encoding);
* Encode a header value (not including its label) optimally.
* Picks shortest of Q, B, or none. Result includes folding if needed.
* See RFC822 definitions for phrase, comment and text positions.
* @param string $str The header value to encode
* @param string $position What context the string will be used in
public function encodeHeader($str, $position = 'text')
switch (strtolower($position)) {
if (!preg_match('/[\200-\377]/', $str)) {
//Can't use addslashes as we don't know the value of magic_quotes_sybase
$encoded = addcslashes($str, "\0..\37\177\\\"");
if (($str === $encoded) && !preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $str)) {
$matchcount = preg_match_all('/[^\040\041\043-\133\135-\176]/', $str, $matches);
/* @noinspection PhpMissingBreakStatementInspection */
$matchcount = preg_match_all('/[()"]/', $str, $matches);
$matchcount += preg_match_all('/[\000-\010\013\014\016-\037\177-\377]/', $str, $matches);
if ($this->has8bitChars($str)) {
$charset = $this->CharSet;
$charset = static::CHARSET_ASCII;
//Q/B encoding adds 8 chars and the charset ("` =?<charset>?[QB]?<content>?=`").
$overhead = 8 + strlen($charset);
if ('mail' === $this->Mailer) {
$maxlen = static::MAIL_MAX_LINE_LENGTH - $overhead;
$maxlen = static::MAX_LINE_LENGTH - $overhead;
//Select the encoding that produces the shortest output and/or prevents corruption.
if ($matchcount > strlen($str) / 3) {
//More than 1/3 of the content needs encoding, use B-encode.
} elseif ($matchcount > 0) {
//Less than 1/3 of the content needs encoding, use Q-encode.
} elseif (strlen($str) > $maxlen) {
//No encoding needed, but value exceeds max line length, use Q-encode to prevent corruption.
if ($this->hasMultiBytes($str)) {
//Use a custom function which correctly encodes and wraps long
//multibyte strings without breaking lines within a character
$encoded = $this->base64EncodeWrapMB($str, "\n");
$encoded = base64_encode($str);
$encoded = trim(chunk_split($encoded, $maxlen, "\n"));
$encoded = preg_replace('/^(.*)$/m', ' =?' . $charset . "?$encoding?\\1?=", $encoded);
$encoded = $this->encodeQ($str, $position);
$encoded = $this->wrapText($encoded, $maxlen, true);
$encoded = str_replace('=' . static::$LE, "\n", trim($encoded));
$encoded = preg_replace('/^(.*)$/m', ' =?' . $charset . "?$encoding?\\1?=", $encoded);
return trim(static::normalizeBreaks($encoded));
* Check if a string contains multi-byte characters.
* @param string $str multi-byte text to wrap encode
public function hasMultiBytes($str)
if (function_exists('mb_strlen')) {
return strlen($str) > mb_strlen($str, $this->CharSet);
//Assume no multibytes (we can't handle without mbstring functions anyway)
* Does a string contain any 8-bit chars (in any charset)?
public function has8bitChars($text)
return (bool) preg_match('/[\x80-\xFF]/', $text);
* Encode and wrap long multibyte strings for mail headers
* without breaking lines within a character.
* Adapted from a function by paravoid.
* @see http://www.php.net/manual/en/function.mb-encode-mimeheader.php#60283
* @param string $str multi-byte text to wrap encode
* @param string $linebreak string to use as linefeed/end-of-line
public function base64EncodeWrapMB($str, $linebreak = null)
$start = '=?' . $this->CharSet . '?B?';
if (null === $linebreak) {
$linebreak = static::$LE;
$mb_length = mb_strlen($str, $this->CharSet);
//Each line must have length <= 75, including $start and $end
$length = 75 - strlen($start) - strlen($end);
//Average multi-byte ratio
$ratio = $mb_length / strlen($str);
$avgLength = floor($length * $ratio * .75);
for ($i = 0; $i < $mb_length; $i += $offset) {
$offset = $avgLength - $lookBack;
$chunk = mb_substr($str, $i, $offset, $this->CharSet);
$chunk = base64_encode($chunk);
} while (strlen($chunk) > $length);
$encoded .= $chunk . $linebreak;
//Chomp the last linefeed
return substr($encoded, 0, -strlen($linebreak));
* Encode a string in quoted-printable format.
* According to RFC2045 section 6.7.
* @param string $string The text to encode
public function encodeQP($string)
return static::normalizeBreaks(quoted_printable_encode($string));
* Encode a string using Q encoding.
* @see http://tools.ietf.org/html/rfc2047#section-4.2
* @param string $str the text to encode
* @param string $position Where the text is going to be used, see the RFC for what that means
public function encodeQ($str, $position = 'text')
//There should not be any EOL in the string
$encoded = str_replace(["\r", "\n"], '', $str);
switch (strtolower($position)) {
$pattern = '^A-Za-z0-9!*+\/ -';
* Build $pattern without including delimiters and []
/* @noinspection PhpMissingBreakStatementInspection */
/* Intentional fall through */
//Replace every high ascii, control, =, ? and _ characters
$pattern = '\000-\011\013\014\016-\037\075\077\137\177-\377' . $pattern;
if (preg_match_all("/[{$pattern}]/", $encoded, $matches)) {
//If the string contains an '=', make sure it's the first thing we replace
//so as to avoid double-encoding
$eqkey = array_search('=', $matches[0], true);
unset($matches[0][$eqkey]);
array_unshift($matches[0], '=');
foreach (array_unique($matches[0]) as $char) {
$encoded = str_replace($char, '=' . sprintf('%02X', ord($char)), $encoded);
//Replace spaces with _ (more readable than =20)
//RFC 2047 section 4.2(2)
return str_replace(' ', '_', $encoded);
* Add a string or binary attachment (non-filesystem).
* This method can be used to attach ascii or binary data,
* such as a BLOB record from a database.
* @param string $string String attachment data
* @param string $filename Name of the attachment
* @param string $encoding File encoding (see $Encoding)
* @param string $type File extension (MIME) type
* @param string $disposition Disposition to use
* @return bool True on successfully adding an attachment
public function addStringAttachment(
$encoding = self::ENCODING_BASE64,
$disposition = 'attachment'
//If a MIME type is not specified, try to work it out from the file name
$type = static::filenameToType($filename);
if (!$this->validateEncoding($encoding)) {
throw new Exception($this->lang('encoding') . $encoding);
//Append to $attachment array
2 => static::mb_pathinfo($filename, PATHINFO_BASENAME),
5 => true, //isStringAttachment
} catch (Exception $exc) {
$this->setError($exc->getMessage());
$this->edebug($exc->getMessage());
* Add an embedded (inline) attachment from a file.
* This can include images, sounds, and just about any other document type.
* These differ from 'regular' attachments in that they are intended to be
* displayed inline with the message, not just attached for download.
* This is used in HTML messages that embed the images
* the HTML refers to using the `$cid` value in `img` tags, for example `<img src="cid:mylogo">`.
* Never use a user-supplied path to a file!
* @param string $path Path to the attachment
* @param string $cid Content ID of the attachment; Use this to reference
* the content when using an embedded image in HTML
* @param string $name Overrides the attachment filename
* @param string $encoding File encoding (see $Encoding) defaults to `base64`
* @param string $type File MIME type (by default mapped from the `$path` filename's extension)
* @param string $disposition Disposition to use: `inline` (default) or `attachment`
* (unlikely you want this – {@see `addAttachment()`} instead)
* @return bool True on successfully adding an attachment
public function addEmbeddedImage(
$encoding = self::ENCODING_BASE64,
if (!static::fileIsAccessible($path)) {
throw new Exception($this->lang('file_access') . $path, self::STOP_CONTINUE);
//If a MIME type is not specified, try to work it out from the file name
$type = static::filenameToType($path);
if (!$this->validateEncoding($encoding)) {
throw new Exception($this->lang('encoding') . $encoding);
$filename = (string) static::mb_pathinfo($path, PATHINFO_BASENAME);
//Append to $attachment array
5 => false, //isStringAttachment
} catch (Exception $exc) {
$this->setError($exc->getMessage());
$this->edebug($exc->getMessage());
* Add an embedded stringified attachment.
* This can include images, sounds, and just about any other document type.
* If your filename doesn't contain an extension, be sure to set the $type to an appropriate MIME type.
* @param string $string The attachment binary data
* @param string $cid Content ID of the attachment; Use this to reference
* the content when using an embedded image in HTML
* @param string $name A filename for the attachment. If this contains an extension,
* PHPMailer will attempt to set a MIME type for the attachment.
* For example 'file.jpg' would get an 'image/jpeg' MIME type.
* @param string $encoding File encoding (see $Encoding), defaults to 'base64'
* @param string $type MIME type - will be used in preference to any automatically derived type
* @param string $disposition Disposition to use
* @return bool True on successfully adding an attachment
public function addStringEmbeddedImage(
$encoding = self::ENCODING_BASE64,
//If a MIME type is not specified, try to work it out from the name
if ('' === $type && !empty($name)) {
$type = static::filenameToType($name);
if (!$this->validateEncoding($encoding)) {
throw new Exception($this->lang('encoding') . $encoding);
//Append to $attachment array
5 => true, //isStringAttachment
} catch (Exception $exc) {
$this->setError($exc->getMessage());
$this->edebug($exc->getMessage());
* @param string $encoding
protected function validateEncoding($encoding)
self::ENCODING_QUOTED_PRINTABLE,
* Check if an embedded attachment is present with this cid.
protected function cidExists($cid)
foreach ($this->attachment as $attachment) {
if ('inline' === $attachment[6] && $cid === $attachment[7]) {
* Check if an inline attachment is present.
public function inlineImageExists()
foreach ($this->attachment as $attachment) {
if ('inline' === $attachment[6]) {
* Check if an attachment (non-inline) is present.
public function attachmentExists()
foreach ($this->attachment as $attachment) {
if ('attachment' === $attachment[6]) {