[Patch] Rewrite of Mail_Mime's _quotedPrintableEncode()
| From: | Matthias Pigulla | Date: | Sun, 19 Sep 2004 15:29:00 +0000 |
| Subject: | [Patch] Rewrite of Mail_Mime's _quotedPrintableEncode() | ||
| Groups: | php.pear.dev | ||
| Request: | Send a blank email to pear-dev+get-33449@lists.php.net to get a copy of this message | ||
Hi all,
this patch is a rewrite of Mail_Mime's
Mail_mimePart::_quotedPrintableEncode (mimePart.php). Basically, I wrote
it because I have to create personalized (mime) messages for a large
amount of recipients and had to optimize performance (processing time
and memory) somewhat.
The rewrite does not preg_split the input at newlines and thus reduces
memory usage; further, it uses strspn to find substrings of the input
that do not need to be encoded. That avoids having to slice out every
single character from the input by calling substr.
Note 1 - the call to strspn requires the two additional parameters
introduced in PHP 4.3.0; I don't know if that violates any PEAR
standards? (I haven't contributed to PEAR before, so I'm somewhat new to
this.)
Note 2 - The old implementation did not encode TAB characters
immediately followed by a newline (hard line break) as required by
RFC2045, section 6.7 paragraph (3). Maybe you would like to fix that
even if rejecting this patch.
Best regards,
Matthias
Index: mimePart.php =================================================================== RCS file: /repository/pear/Mail_Mime/mimePart.php,v retrieving revision 1.11 diff -u -r1.11 mimePart.php --- mimePart.php 4 Jan 2003 11:55:34 -0000 1.11 +++ mimePart.php 19 Sep 2004 15:00:55 -0000 @@ -1,6 +1,6 @@ <?php // +-----------------------------------------------------------------------+ -// | Copyright (c) 2002-2003 Richard Heyes | +// | Copyright (c) 2002-2003 Richard Heyes | // | All rights reserved. | // | | // | Redistribution and use in source and binary forms, with or without | @@ -299,9 +299,10 @@ } /** - * quoteadPrintableEncode() + * _quotedPrintableEncode() * - * Encodes data to quoted-printable standard. + * Encodes data to quoted-printable standard. Single CR (\r) will be preserved, + * LF (\n) and CRLF (\r\n) will be rewritten to MAIL_MIMEPART_CRLF. * * @param $input The data to encode * @param $line_max Optional max line length. Should @@ -309,41 +310,77 @@ * * @access private */ - function _quotedPrintableEncode($input , $line_max = 76) + function _quotedPrintableEncode($s, $maxlen = 76) { - $lines = preg_split("/\r?\n/", $input); - $eol = MAIL_MIMEPART_CRLF; - $escape = '='; - $output = ''; - - while(list(, $line) = each($lines)){ - - $linlen = strlen($line); - $newline = ''; - - for ($i = 0; $i < $linlen; $i++) { - $char = substr($line, $i, 1); - $dec = ord($char); - - if (($dec == 32) AND ($i == ($linlen - 1))){ // convert space at eol only - $char = '=20'; - - } elseif($dec == 9) { - ; // Do nothing if a tab. - } elseif(($dec == 61) OR ($dec < 32 ) OR ($dec > 126)) { - $char = $escape . strtoupper(sprintf('%02s', dechex($dec))); + $out = ''; // buffer for encoded string + $outlinelen = 0; // chars on current line (since last hard or soft break) + $pos = 0; // position in input string + $inputlen = strlen($s); + + static $goodchars = null; // for performance reasons, don't rebuild $goodchars upon every invocation + if (!$goodchars) { + $goodchars = chr(9); + for($i = 32; $i <= 126; $i++) { + if ($i != 61) { + $goodchars .= chr($i); + } } + } + + for($pos = 0; $pos < $inputlen; ) { + $i = strspn($s, $goodchars, $pos, $maxlen - $outlinelen); // find next substring consisting of "good" chars only, but not more than would fit on current encoded line - if ((strlen($newline) + strlen($char)) >= $line_max) { // MAIL_MIMEPART_CRLF is not counted - $output .= $newline . $escape . $eol; // soft line break; " =\r\n" is okay - $newline = ''; + if ($i > 0) { // found "good" chars + + if ($outlinelen + $i == $maxlen) { // fills up current line + $i -= 1; + $out .= substr($s, $pos, $i) . '=' . MAIL_MIMEPART_CRLF; + $outlinelen = 0; + } else { + $out .= substr($s, $pos, $i); + $outlinelen += $i; } - $newline .= $char; - } // end of for - $output .= $newline . $eol; - } - $output = substr($output, 0, -1 * strlen($eol)); // Don't want last crlf - return $output; + $pos += $i; + + } else { // next char needs encoding + + $char = substr($s, $pos, 1); + if ($char == "\r" && substr($s, $pos + 1, 1) == "\n") { + // \r, but \n will follow. Just ignore \r for now and handle \n on next iteration. + // \r\n => MAIL_MIMEPART_CRLF + } elseif($char == "\n") { + // Hard line break. \n => MAIL_MIMEPART_CRLF + + // However: If last char was ASCII 9/32 (tab/space), encode it. + if (($lastord = ord(substr($out, -1, 1))) == 9 || $lastord == 32) { + $out = substr($out, 0, -1); $outlinelen--; // Take last char from line. + if ($outlinelen + 3 >= $maxlen) { // Last char does not fit on line if encoded. Add soft break. + $out .= '=' . MAIL_MIMEPART_CRLF; + $outlinelen = 0; + } + $out .= strtoupper(sprintf('=%02s', dechex($lastord))); // Add last char to line again - encoded! + $outlinelen += 3; + } + + // Add the hard line break itself. + $out .= MAIL_MIMEPART_CRLF; + $outlinelen = 0; + } else { + // Just a single char that needs to be encoded. + + if ($outlinelen + 3 >= $maxlen) { // Char does not fit on line if encoded. Add soft break. + $out .= '=' . MAIL_MIMEPART_CRLF; + $outlinelen = 0; + } + + // Add the encoded char to line. + $out .= (strtoupper(sprintf('=%02s', dechex(ord($char))))); + $outlinelen += 3; + } + $pos++; + } // end if good/bad + } // end for + return $out; } } // End of class ?>
Index: mimePart.php =================================================================== RCS file: /repository/pear/Mail_Mime/mimePart.php,v retrieving revision 1.11 diff -u -r1.11 mimePart.php --- mimePart.php 4 Jan 2003 11:55:34 -0000 1.11 +++ mimePart.php 19 Sep 2004 15:00:55 -0000 @@ -1,6 +1,6 @@ <?php // +-----------------------------------------------------------------------+ -// | Copyright (c) 2002-2003 Richard Heyes | +// | Copyright (c) 2002-2003 Richard Heyes | // | All rights reserved. | // | | // | Redistribution and use in source and binary forms, with or without | @@ -299,9 +299,10 @@ } /** - * quoteadPrintableEncode() + * _quotedPrintableEncode() * - * Encodes data to quoted-printable standard. + * Encodes data to quoted-printable standard. Single CR (\r) will be preserved, + * LF (\n) and CRLF (\r\n) will be rewritten to MAIL_MIMEPART_CRLF. * * @param $input The data to encode * @param $line_max Optional max line length. Should @@ -309,41 +310,77 @@ * * @access private */ - function _quotedPrintableEncode($input , $line_max = 76) + function _quotedPrintableEncode($s, $maxlen = 76) { - $lines = preg_split("/\r?\n/", $input); - $eol = MAIL_MIMEPART_CRLF; - $escape = '='; - $output = ''; - - while(list(, $line) = each($lines)){ - - $linlen = strlen($line); - $newline = ''; - - for ($i = 0; $i < $linlen; $i++) { - $char = substr($line, $i, 1); - $dec = ord($char); - - if (($dec == 32) AND ($i == ($linlen - 1))){ // convert space at eol only - $char = '=20'; - - } elseif($dec == 9) { - ; // Do nothing if a tab. - } elseif(($dec == 61) OR ($dec < 32 ) OR ($dec > 126)) { - $char = $escape . strtoupper(sprintf('%02s', dechex($dec))); + $out = ''; // buffer for encoded string + $outlinelen = 0; // chars on current line (since last hard or soft break) + $pos = 0; // position in input string + $inputlen = strlen($s); + + static $goodchars = null; // for performance reasons, don't rebuild $goodchars upon every invocation + if (!$goodchars) { + $goodchars = chr(9); + for($i = 32; $i <= 126; $i++) { + if ($i != 61) { + $goodchars .= chr($i); + } } + } + + for($pos = 0; $pos < $inputlen; ) { + $i = strspn($s, $goodchars, $pos, $maxlen - $outlinelen); // find next substring consisting of "good" chars only, but not more than would fit on current encoded line - if ((strlen($newline) + strlen($char)) >= $line_max) { // MAIL_MIMEPART_CRLF is not counted - $output .= $newline . $escape . $eol; // soft line break; " =\r\n" is okay - $newline = ''; + if ($i > 0) { // found "good" chars + + if ($outlinelen + $i == $maxlen) { // fills up current line + $i -= 1; + $out .= substr($s, $pos, $i) . '=' . MAIL_MIMEPART_CRLF; + $outlinelen = 0; + } else { + $out .= substr($s, $pos, $i); + $outlinelen += $i; } - $newline .= $char; - } // end of for - $output .= $newline . $eol; - } - $output = substr($output, 0, -1 * strlen($eol)); // Don't want last crlf - return $output; + $pos += $i; + + } else { // next char needs encoding + + $char = substr($s, $pos, 1); + if ($char == "\r" && substr($s, $pos + 1, 1) == "\n") { + // \r, but \n will follow. Just ignore \r for now and handle \n on next iteration. + // \r\n => MAIL_MIMEPART_CRLF + } elseif($char == "\n") { + // Hard line break. \n => MAIL_MIMEPART_CRLF + + // However: If last char was ASCII 9/32 (tab/space), encode it. + if (($lastord = ord(substr($out, -1, 1))) == 9 || $lastord == 32) { + $out = substr($out, 0, -1); $outlinelen--; // Take last char from line. + if ($outlinelen + 3 >= $maxlen) { // Last char does not fit on line if encoded. Add soft break. + $out .= '=' . MAIL_MIMEPART_CRLF; + $outlinelen = 0; + } + $out .= strtoupper(sprintf('=%02s', dechex($lastord))); // Add last char to line again - encoded! + $outlinelen += 3; + } + + // Add the hard line break itself. + $out .= MAIL_MIMEPART_CRLF; + $outlinelen = 0; + } else { + // Just a single char that needs to be encoded. + + if ($outlinelen + 3 >= $maxlen) { // Char does not fit on line if encoded. Add soft break. + $out .= '=' . MAIL_MIMEPART_CRLF; + $outlinelen = 0; + } + + // Add the encoded char to line. + $out .= (strtoupper(sprintf('=%02s', dechex(ord($char))))); + $outlinelen += 3; + } + $pos++; + } // end if good/bad + } // end for + return $out; } } // End of class ?>