1: <?php
2:
3: /**
4: * Jyxo PHP Library
5: *
6: * LICENSE
7: *
8: * This source file is subject to the new BSD license that is bundled
9: * with this package in the file license.txt.
10: * It is also available through the world-wide-web at this URL:
11: * https://github.com/jyxo/php/blob/master/license.txt
12: */
13:
14: /**
15: * List of possible content encodings.
16: *
17: * @category Jyxo
18: * @package Jyxo_Mail
19: * @copyright Copyright (c) 2005-2011 Jyxo, s.r.o.
20: * @license https://github.com/jyxo/php/blob/master/license.txt
21: * @author Jaroslav HanslĂk
22: */
23: class Jyxo_Mail_Encoding
24: {
25: /**
26: * 8-bit encoding.
27: *
28: * @var string
29: */
30: const BIT8 = '8bit';
31:
32: /**
33: * 7-bit encoding.
34: *
35: * @var string
36: */
37: const BIT7 = '7bit';
38:
39: /**
40: * Binary encoding.
41: *
42: * @var string
43: */
44: const BINARY = 'binary';
45:
46: /**
47: * Base64 encoding.
48: *
49: * @var string
50: */
51: const BASE64 = 'base64';
52:
53: /**
54: * Quoted printable encoding.
55: *
56: * @var string
57: */
58: const QUOTED_PRINTABLE = 'quoted-printable';
59:
60: /**
61: * Constructor preventing from creating instances.
62: *
63: * @throws LogicException When trying to create an instance
64: */
65: public final function __construct()
66: {
67: throw new LogicException(sprintf('Cannot create an instance of a static class %s.', get_class($this)));
68: }
69:
70: /**
71: * Checks if the given encoding is compatible.
72: *
73: * @param string $encoding Encoding name
74: * @return boolean
75: */
76: public static function isCompatible($encoding)
77: {
78: static $encodings = array(
79: self::BIT7 => true,
80: self::BIT8 => true,
81: self::BINARY => true,
82: self::BASE64 => true,
83: self::QUOTED_PRINTABLE => true
84: );
85:
86: return isset($encodings[$encoding]);
87: }
88:
89: /**
90: * Encodes a string using the given encoding.
91: *
92: * @param string $string Input string
93: * @param string $encoding Encoding name
94: * @param integer $lineLength Line length
95: * @param string $lineEnd Line ending
96: * @return string
97: * @throws InvalidArgumentException If an incompatible encoding was provided
98: */
99: public static function encode($string, $encoding, $lineLength, $lineEnd)
100: {
101: switch ($encoding) {
102: case self::BASE64:
103: return self::encodeBase64($string, $lineLength, $lineEnd);
104: case self::BIT7:
105: // Break missing intentionally
106: case self::BIT8:
107: return Jyxo_String::fixLineEnding(trim($string), $lineEnd) . $lineEnd;
108: case self::QUOTED_PRINTABLE:
109: return self::encodeQuotedPrintable($string, $lineLength, $lineEnd);
110: case self::BINARY:
111: return $string;
112: default:
113: throw new InvalidArgumentException(sprintf('Incompatible encoding %s.', $encoding));
114: }
115: }
116:
117: /**
118: * Encodes a string using the quoted-printable encoding.
119: *
120: * @param string $string Input string
121: * @param integer $lineLength Line length
122: * @param string $lineEnd Line ending
123: * @return string
124: */
125: private static function encodeQuotedPrintable($string, $lineLength, $lineEnd)
126: {
127: $encoded = Jyxo_String::fixLineEnding(trim($string), $lineEnd);
128:
129: // Replaces all high ASCII characters, control codes and '='
130: $encoded = preg_replace_callback('~([\000-\010\013\014\016-\037\075\177-\377])~', function($matches) {
131: return '=' . sprintf('%02X', ord($matches[1]));
132: }, $encoded);
133:
134: // Replaces tabs and spaces if on line ends
135: $encoded = preg_replace_callback('~([\011\040])' . $lineEnd . '~', function($matches) use ($lineEnd) {
136: return '=' . sprintf('%02X', ord($matches[1])) . $lineEnd;
137: }, $encoded);
138:
139: $output = '';
140: $lines = explode($lineEnd, $encoded);
141: // Release from memory
142: unset($encoded);
143: foreach ($lines as $line) {
144: // Line length is less than maximum
145: if (strlen($line) <= $lineLength) {
146: $output .= $line . $lineEnd;
147: continue;
148: }
149:
150: do {
151: $partLength = strlen($line);
152: if ($partLength > $lineLength) {
153: $partLength = $lineLength;
154: }
155:
156: // Cannot break a line in the middle of a character
157: $pos = strrpos(substr($line, 0, $partLength), '=');
158: if ((false !== $pos) && ($pos >= $partLength - 2)) {
159: $partLength = $pos;
160: }
161:
162: // If the last char is a break, move one character backwards
163: if (($partLength > 0) && (' ' == $line[$partLength - 1])) {
164: $partLength--;
165: }
166:
167: // Saves string parts, trims the string and continues
168: $output .= substr($line, 0, $partLength);
169: $line = substr($line, $partLength);
170:
171: // We are in the middle of a line
172: if (!empty($line)) {
173: $output .= '=';
174: }
175: $output .= $lineEnd;
176: } while (!empty($line));
177: }
178:
179: return $output;
180: }
181:
182: /**
183: * Encodes a string using the base64 encoding.
184: *
185: * @param string $string Input string
186: * @param integer $lineLength Line length
187: * @param string $lineEnd Line ending
188: * @return string
189: */
190: private static function encodeBase64($string, $lineLength, $lineEnd)
191: {
192: return trim(chunk_split(base64_encode($string), $lineLength, $lineEnd));
193: }
194: }
195: