1 | <?php |
||
2 | |||
3 | /** |
||
4 | * @package toolkit |
||
5 | */ |
||
6 | |||
7 | /** |
||
8 | * A helper class for various email functions. |
||
9 | */ |
||
10 | abstract class EmailHelper |
||
11 | { |
||
12 | /** |
||
13 | * Folding an email header field body as required by RFC2822. |
||
14 | * |
||
15 | * @param string $input |
||
16 | * header field body string |
||
17 | * @param integer $max_length |
||
18 | * defaults to 75 |
||
19 | * @return string |
||
20 | * folded output string |
||
21 | */ |
||
22 | public static function fold($input, $max_length = 75) |
||
0 ignored issues
–
show
Coding Style
introduced
by
![]() |
|||
23 | { |
||
24 | return @wordwrap($input, $max_length, "\r\n "); |
||
25 | } |
||
26 | |||
27 | /** |
||
28 | * Q-encoding of a header field 'text' token or 'word' entity |
||
29 | * within a 'phrase', according to RFC2047. The output is called |
||
30 | * an 'encoded-word'; it must not be longer than 75 characters. |
||
31 | * |
||
32 | * This might be achieved with PHP's `mbstring` functions, but |
||
33 | * `mbstring` is a non-default extension. |
||
34 | * |
||
35 | * For simplicity reasons this function encodes every character |
||
36 | * except upper and lower case letters and decimal digits. |
||
37 | * |
||
38 | * RFC: 'While there is no limit to the length of a multiple-line |
||
39 | * header field, each line of a header field that contains one or |
||
40 | * more 'encoded-word's is limited to 76 characters.' |
||
41 | * The required 'folding' will not be done here, but in another |
||
42 | * helper function. |
||
43 | * |
||
44 | * This function must be 'multi-byte-sensitive' in a way that it |
||
45 | * must never scatter a multi-byte character representation across |
||
46 | * multiple encoded-words. So a 'lookahead' has been implemented, |
||
47 | * based on the fact that for UTF-8 encoded characters any byte |
||
48 | * except the first byte will have a leading '10' bit pattern, |
||
49 | * which means an ASCII value >=128 and <=191. |
||
50 | * |
||
51 | * @author Elmar Bartel |
||
52 | * @author Michael Eichelsdoerfer |
||
53 | * @param string $input |
||
54 | * string to encode |
||
55 | * @param integer $max_length |
||
56 | * maximum line length (default: 75 chars) |
||
57 | * @return string $output |
||
58 | * encoded string |
||
59 | */ |
||
60 | public static function qEncode($input, $max_length = 75) |
||
0 ignored issues
–
show
|
|||
61 | { |
||
62 | // Don't encode empty strings |
||
63 | if (empty($input)) { |
||
64 | return $input; |
||
65 | } |
||
66 | |||
67 | // Don't encode if all code points are in ASCII range 0-127 |
||
68 | if (!preg_match('/[\\x80-\\xff]+/', $input)) { |
||
69 | return $input; |
||
70 | } |
||
71 | |||
72 | $qpHexDigits = '0123456789ABCDEF'; |
||
73 | $input_length = strlen($input); |
||
74 | // Substract delimiters, character set and encoding |
||
75 | $line_limit = $max_length - 12; |
||
76 | $line_length = 0; |
||
77 | |||
78 | $output = '=?UTF-8?Q?'; |
||
79 | |||
80 | for ($i=0; $i < $input_length; $i++) { |
||
81 | $char = $input[$i]; |
||
82 | $ascii = ord($char); |
||
83 | |||
84 | // No encoding for all 62 alphanumeric characters |
||
85 | if (48 <= $ascii && $ascii <= 57 || 65 <= $ascii && $ascii <= 90 || 97 <= $ascii && $ascii <= 122) { |
||
86 | $replace_length = 1; |
||
87 | $replace_char = $char; |
||
88 | |||
89 | // Encode space as underscore (means better readability for humans) |
||
90 | } elseif ($ascii == 32) { |
||
91 | $replace_length = 1; |
||
92 | $replace_char = '_'; |
||
93 | |||
94 | // Encode |
||
95 | } else { |
||
96 | $replace_length = 3; |
||
97 | // Bit operation is around 10 percent faster |
||
98 | // than 'strtoupper(dechex($ascii))' |
||
99 | $replace_char = '=' |
||
100 | . $qpHexDigits[$ascii >> 4] |
||
101 | . $qpHexDigits[$ascii & 0x0f]; |
||
102 | |||
103 | // Account for following bytes of UTF8-multi-byte |
||
104 | // sequence (max. length is 4 octets, RFC3629) |
||
105 | $lookahead_limit = min($i+4, $input_length); |
||
106 | |||
107 | for ($lookahead = $i+1; $lookahead < $lookahead_limit; $lookahead++) { |
||
108 | $ascii_ff = ord($input[$lookahead]); |
||
109 | if (128 <= $ascii_ff && $ascii_ff <= 191) { |
||
110 | $replace_char .= '=' |
||
111 | . $qpHexDigits[$ascii_ff >> 4] |
||
112 | . $qpHexDigits[$ascii_ff & 0x0f]; |
||
113 | $replace_length += 3; |
||
114 | $i++; |
||
115 | } else { |
||
116 | break; |
||
117 | } |
||
118 | } |
||
119 | } |
||
120 | |||
121 | // Would the line become too long? |
||
122 | if ($line_length + $replace_length > $line_limit) { |
||
123 | $output .= "?= =?UTF-8?Q?"; |
||
0 ignored issues
–
show
Coding Style
Comprehensibility
introduced
by
The string literal
?= =?UTF-8?Q? does not require double quotes, as per coding-style, please use single quotes.
PHP provides two ways to mark string literals. Either with single quotes String literals in single quotes on the other hand are evaluated very literally and the only two
characters that needs escaping in the literal are the single quote itself ( Double quoted string literals may contain other variables or more complex escape sequences. <?php
$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";
print $doubleQuoted;
will print an indented: If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear. For more information on PHP string literals and available escape sequences see the PHP core documentation. ![]() |
|||
124 | $line_length = 0; |
||
125 | } |
||
126 | |||
127 | $output .= $replace_char; |
||
128 | $line_length += $replace_length; |
||
129 | } |
||
130 | |||
131 | $output .= '?='; |
||
132 | return $output; |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Quoted-printable encoding of a message body (part), |
||
137 | * according to RFC2045. |
||
138 | * |
||
139 | * This function handles <CR>, <LF>, <CR><LF> and <LF><CR> sequences |
||
140 | * as 'user relevant' line breaks and encodes them as RFC822 line |
||
141 | * breaks as required by RFC2045. |
||
142 | * |
||
143 | * @author Elmar Bartel |
||
144 | * @author Michael Eichelsdoerfer |
||
145 | * @param string $input |
||
146 | * string to encode |
||
147 | * @param integer $max_length |
||
148 | * maximum line length (default: 76 chars) |
||
149 | * @return string $output |
||
150 | * encoded string |
||
151 | */ |
||
152 | public static function qpContentTransferEncode($input, $max_length = 76) |
||
0 ignored issues
–
show
|
|||
153 | { |
||
154 | $qpHexDigits = '0123456789ABCDEF'; |
||
155 | $input_length = strlen($input); |
||
156 | $line_limit = $max_length; |
||
157 | $line_length = 0; |
||
158 | $output = ''; |
||
159 | $blank = false; |
||
160 | |||
161 | for ($i=0; $i < $input_length; $i++) { |
||
162 | $char = $input[$i]; |
||
163 | $ascii = ord($char); |
||
164 | |||
165 | // No encoding for spaces and tabs |
||
166 | if ($ascii == 9 || $ascii == 32) { |
||
167 | $blank = true; |
||
168 | $replace_length = 1; |
||
169 | $replace_char = $char; |
||
170 | |||
171 | // CR and LF |
||
172 | } elseif ($ascii == 13 || $ascii == 10) { |
||
173 | // Use existing offset only. |
||
174 | if ($i+1 < $input_length) { |
||
175 | if (($ascii == 13 && ord($input[$i+1]) == 10) || ($ascii == 10 && ord($input[$i+1]) == 13)) { |
||
176 | $i++; |
||
177 | } |
||
178 | } |
||
179 | |||
180 | if ($blank) { |
||
181 | /** |
||
182 | * Any tab or space characters on an encoded line MUST |
||
183 | * be followed on that line by a printable character. |
||
184 | * This character may as well be the soft line break |
||
185 | * indicator. |
||
186 | * |
||
187 | * So if the preceding character is a space or a |
||
188 | * tab, we may simply insert a soft line break |
||
189 | * here, followed by a literal line break. |
||
190 | * Basically this means that we are appending |
||
191 | * an empty line (nada). |
||
192 | */ |
||
193 | $output .= "=\r\n\r\n"; |
||
194 | } else { |
||
195 | $output .= "\r\n"; |
||
196 | } |
||
197 | |||
198 | $blank = false; |
||
199 | $line_length = 0; |
||
200 | continue; |
||
201 | |||
202 | // No encoding within ascii range 33 to 126 (exception: 61) |
||
203 | } elseif (32 < $ascii && $ascii < 127 && $char !== '=') { |
||
204 | $replace_length = 1; |
||
205 | $replace_char = $char; |
||
206 | $blank = false; |
||
207 | |||
208 | // Encode |
||
209 | } else { |
||
210 | $replace_length = 3; |
||
211 | // bit operation is around 10 percent faster |
||
212 | // than 'strtoupper(dechex($ascii))' |
||
213 | $replace_char = '=' |
||
214 | . $qpHexDigits[$ascii >> 4] |
||
215 | . $qpHexDigits[$ascii & 0x0f]; |
||
216 | $blank = false; |
||
217 | } |
||
0 ignored issues
–
show
|
|||
218 | // Would the line become too long? |
||
219 | if ($line_length + $replace_length > $line_limit - 1) { |
||
220 | $output .= "=\r\n"; |
||
221 | $line_length = 0; |
||
222 | } |
||
223 | |||
224 | $output .= $replace_char; |
||
225 | $line_length += $replace_length; |
||
226 | } |
||
227 | |||
228 | return $output; |
||
229 | } |
||
230 | |||
231 | /** |
||
232 | * Content-Transfer-Encoding for attachments |
||
233 | * |
||
234 | * This function will encode attachments according to RFC2045. |
||
235 | * Line length must not exceed the default (76 characters). |
||
236 | * |
||
237 | * @author Michael Eichelsdoerfer |
||
238 | * @param string $data |
||
239 | * @param integer $length |
||
240 | * @return string |
||
241 | */ |
||
242 | public static function base64ContentTransferEncode($data, $length = 76) |
||
0 ignored issues
–
show
|
|||
243 | { |
||
244 | return chunk_split(base64_encode($data), $length); |
||
245 | } |
||
246 | |||
247 | /** |
||
248 | * Implodes an associative array or straight array to a |
||
249 | * comma-separated string |
||
250 | * |
||
251 | * @param array $array |
||
252 | * @return string |
||
253 | */ |
||
254 | public static function arrayToList(array $array = array()) |
||
0 ignored issues
–
show
|
|||
255 | { |
||
256 | $return = array(); |
||
257 | foreach ($array as $name => $email) { |
||
258 | $return[] = empty($name) || General::intval($name) > -1 |
||
259 | |||
0 ignored issues
–
show
|
|||
260 | : $name . ' <' . $email . '>'; |
||
261 | } |
||
262 | |||
263 | return implode(', ', $return); |
||
264 | } |
||
265 | } |
||
266 |