: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* //Use your own custom converter
* $plain = $mail->html2text($html, function($html) {
* $converter = new MyHtml2text($html);
* return $converter->get_text();
* @param string $html The HTML text to convert
* @param bool|callable $advanced Any boolean value to use the internal converter,
* or provide your own callable for custom conversion.
* *Never* pass user-supplied data into this parameter
public function html2text($html, $advanced = false)
if (is_callable($advanced)) {
return call_user_func($advanced, $html);
return html_entity_decode(
trim(strip_tags(preg_replace('/<(head|title|style|script)[^>]*>.*?<\/\\1>/si', '', $html))),
* Get the MIME type for a file extension.
* @param string $ext File extension
* @return string MIME type of file
public static function _mime_types($ext = '')
'xl' => 'application/excel',
'js' => 'application/javascript',
'hqx' => 'application/mac-binhex40',
'cpt' => 'application/mac-compactpro',
'bin' => 'application/macbinary',
'doc' => 'application/msword',
'word' => 'application/msword',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'sldx' => 'application/vnd.openxmlformats-officedocument.presentationml.slide',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
'class' => 'application/octet-stream',
'dll' => 'application/octet-stream',
'dms' => 'application/octet-stream',
'exe' => 'application/octet-stream',
'lha' => 'application/octet-stream',
'lzh' => 'application/octet-stream',
'psd' => 'application/octet-stream',
'sea' => 'application/octet-stream',
'so' => 'application/octet-stream',
'oda' => 'application/oda',
'pdf' => 'application/pdf',
'ai' => 'application/postscript',
'eps' => 'application/postscript',
'ps' => 'application/postscript',
'smi' => 'application/smil',
'smil' => 'application/smil',
'mif' => 'application/vnd.mif',
'xls' => 'application/vnd.ms-excel',
'ppt' => 'application/vnd.ms-powerpoint',
'wbxml' => 'application/vnd.wap.wbxml',
'wmlc' => 'application/vnd.wap.wmlc',
'dcr' => 'application/x-director',
'dir' => 'application/x-director',
'dxr' => 'application/x-director',
'dvi' => 'application/x-dvi',
'gtar' => 'application/x-gtar',
'php3' => 'application/x-httpd-php',
'php4' => 'application/x-httpd-php',
'php' => 'application/x-httpd-php',
'phtml' => 'application/x-httpd-php',
'phps' => 'application/x-httpd-php-source',
'swf' => 'application/x-shockwave-flash',
'sit' => 'application/x-stuffit',
'tar' => 'application/x-tar',
'tgz' => 'application/x-tar',
'xht' => 'application/xhtml+xml',
'xhtml' => 'application/xhtml+xml',
'zip' => 'application/zip',
'aifc' => 'audio/x-aiff',
'aiff' => 'audio/x-aiff',
'ram' => 'audio/x-pn-realaudio',
'rm' => 'audio/x-pn-realaudio',
'rpm' => 'audio/x-pn-realaudio-plugin',
'ra' => 'audio/x-realaudio',
'mka' => 'audio/x-matroska',
'heifs' => 'image/heif-sequence',
'heics' => 'image/heic-sequence',
'eml' => 'message/rfc822',
'rtx' => 'text/richtext',
'ics' => 'text/calendar',
'wmv' => 'video/x-ms-wmv',
'mov' => 'video/quicktime',
'qt' => 'video/quicktime',
'rv' => 'video/vnd.rn-realvideo',
'avi' => 'video/x-msvideo',
'movie' => 'video/x-sgi-movie',
'mkv' => 'video/x-matroska',
if (array_key_exists($ext, $mimes)) {
return 'application/octet-stream';
* Map a file name to a MIME type.
* Defaults to 'application/octet-stream', i.e.. arbitrary binary data.
* @param string $filename A file name or full path, does not need to exist as a file
public static function filenameToType($filename)
//In case the path is a URL, strip any query string before getting extension
$qpos = strpos($filename, '?');
$filename = substr($filename, 0, $qpos);
$ext = static::mb_pathinfo($filename, PATHINFO_EXTENSION);
return static::_mime_types($ext);
* Multi-byte-safe pathinfo replacement.
* Drop-in replacement for pathinfo(), but multibyte- and cross-platform-safe.
* @see http://www.php.net/manual/en/function.pathinfo.php#107461
* @param string $path A filename or path, does not need to exist as a file
* @param int|string $options Either a PATHINFO_* constant,
* or a string name to return only the specified piece
public static function mb_pathinfo($path, $options = null)
$ret = ['dirname' => '', 'basename' => '', 'extension' => '', 'filename' => ''];
if (preg_match('#^(.*?)[\\\\/]*(([^/\\\\]*?)(\.([^.\\\\/]+?)|))[\\\\/.]*$#m', $path, $pathinfo)) {
if (array_key_exists(1, $pathinfo)) {
$ret['dirname'] = $pathinfo[1];
if (array_key_exists(2, $pathinfo)) {
$ret['basename'] = $pathinfo[2];
if (array_key_exists(5, $pathinfo)) {
$ret['extension'] = $pathinfo[5];
if (array_key_exists(3, $pathinfo)) {
$ret['filename'] = $pathinfo[3];
return $ret['extension'];
* Set or reset instance properties.
* You should avoid this function - it's more verbose, less efficient, more error-prone and
* harder to debug than setting properties directly.
* `$mail->set('SMTPSecure', static::ENCRYPTION_STARTTLS);`
* `$mail->SMTPSecure = static::ENCRYPTION_STARTTLS;`.
* @param string $name The property name to set
* @param mixed $value The value to set the property to
public function set($name, $value = '')
if (property_exists($this, $name)) {
$this->setError($this->lang('variable_set') . $name);
* Strip newlines to prevent header injection.
public function secureHeader($str)
return trim(str_replace(["\r", "\n"], '', $str));
* Normalize line breaks in a string.
* Converts UNIX LF, Mac CR and Windows CRLF line breaks into a single line break format.
* Defaults to CRLF (for message bodies) and preserves consecutive breaks.
* @param string $breaktype What kind of line break to use; defaults to static::$LE
public static function normalizeBreaks($text, $breaktype = null)
if (null === $breaktype) {
$breaktype = static::$LE;
$text = str_replace([self::CRLF, "\r"], "\n", $text);
//Now convert LE as needed
if ("\n" !== $breaktype) {
$text = str_replace("\n", $breaktype, $text);
* Remove trailing whitespace from a string.
* @return string The text to remove whitespace from
public static function stripTrailingWSP($text)
return rtrim($text, " \r\n\t");
* Strip trailing line breaks from a string.
* @return string The text to remove breaks from
public static function stripTrailingBreaks($text)
return rtrim($text, "\r\n");
* Return the current line break format string.
public static function getLE()
* Set the line break format string, e.g. "\r\n".
protected static function setLE($le)
* Set the public and private key files and password for S/MIME signing.
* @param string $cert_filename
* @param string $key_filename
* @param string $key_pass Password for private key
* @param string $extracerts_filename Optional path to chain certificate
public function sign($cert_filename, $key_filename, $key_pass, $extracerts_filename = '')
$this->sign_cert_file = $cert_filename;
$this->sign_key_file = $key_filename;
$this->sign_key_pass = $key_pass;
$this->sign_extracerts_file = $extracerts_filename;
* Quoted-Printable-encode a DKIM header.
public function DKIM_QP($txt)
for ($i = 0; $i < $len; ++$i) {
if (((0x21 <= $ord) && ($ord <= 0x3A)) || $ord === 0x3C || ((0x3E <= $ord) && ($ord <= 0x7E))) {
$line .= '=' . sprintf('%02X', $ord);
* Generate a DKIM signature.
* @param string $signHeader
* @return string The DKIM signature value
public function DKIM_Sign($signHeader)
if (!defined('PKCS7_TEXT')) {
throw new Exception($this->lang('extension_missing') . 'openssl');
$privKeyStr = !empty($this->DKIM_private_string) ?
$this->DKIM_private_string :
file_get_contents($this->DKIM_private);
if ('' !== $this->DKIM_passphrase) {
$privKey = openssl_pkey_get_private($privKeyStr, $this->DKIM_passphrase);
$privKey = openssl_pkey_get_private($privKeyStr);
if (openssl_sign($signHeader, $signature, $privKey, 'sha256WithRSAEncryption')) {
if (\PHP_MAJOR_VERSION < 8) {
openssl_pkey_free($privKey);
return base64_encode($signature);
if (\PHP_MAJOR_VERSION < 8) {
openssl_pkey_free($privKey);
* Generate a DKIM canonicalization header.
* Uses the 'relaxed' algorithm from RFC6376 section 3.4.2.
* Canonicalized headers should *always* use CRLF, regardless of mailer setting.
* @see https://tools.ietf.org/html/rfc6376#section-3.4.2
* @param string $signHeader Header
public function DKIM_HeaderC($signHeader)
//Normalize breaks to CRLF (regardless of the mailer)
$signHeader = static::normalizeBreaks($signHeader, self::CRLF);
//Note PCRE \s is too broad a definition of whitespace; RFC5322 defines it as `[ \t]`
//@see https://tools.ietf.org/html/rfc5322#section-2.2
//That means this may break if you do something daft like put vertical tabs in your headers.
$signHeader = preg_replace('/\r\n[ \t]+/', ' ', $signHeader);
//Break headers out into an array
$lines = explode(self::CRLF, $signHeader);
foreach ($lines as $key => $line) {
//If the header is missing a :, skip it as it's invalid
//This is likely to happen because the explode() above will also split
//on the trailing LE, leaving an empty line
if (strpos($line, ':') === false) {
list($heading, $value) = explode(':', $line, 2);
$heading = strtolower($heading);
//Collapse white space within the value, also convert WSP to space
$value = preg_replace('/[ \t]+/', ' ', $value);
//RFC6376 is slightly unclear here - it says to delete space at the *end* of each value
//But then says to delete space before and after the colon.
//Net result is the same as trimming both ends of the value.
//By elimination, the same applies to the field name
$lines[$key] = trim($heading, " \t") . ':' . trim($value, " \t");
return implode(self::CRLF, $lines);
* Generate a DKIM canonicalization body.
* Uses the 'simple' algorithm from RFC6376 section 3.4.3.
* Canonicalized bodies should *always* use CRLF, regardless of mailer setting.
* @see https://tools.ietf.org/html/rfc6376#section-3.4.3
* @param string $body Message Body
public function DKIM_BodyC($body)
//Normalize line endings to CRLF
$body = static::normalizeBreaks($body, self::CRLF);
//Reduce multiple trailing line breaks to a single one
return static::stripTrailingBreaks($body) . self::CRLF;
* Create the DKIM header and body in a new message header.
* @param string $headers_line Header lines
* @param string $subject Subject
* @param string $body Body
public function DKIM_Add($headers_line, $subject, $body)
$DKIMsignatureType = 'rsa-sha256'; //Signature & hash algorithms
$DKIMcanonicalization = 'relaxed/simple'; //Canonicalization methods of header & body
$DKIMquery = 'dns/txt'; //Query method
//Always sign these headers without being asked
//Recommended list from https://tools.ietf.org/html/rfc6376#section-5.4.1