[Patch] Rewrite of Mail_Mime's _quotedPrintableEncode()

From: 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 ?>
« previous php.pear.dev (#33449) next »