Completed
Push — 5.x ( ef5d14...158fb7 )
by Lars
09:14
created

Swift_Encoder_QpEncoder::encodeString()   C

Complexity

Conditions 12
Paths 50

Size

Total Lines 59
Code Lines 33

Duplication

Lines 21
Ratio 35.59 %

Code Coverage

Tests 35
CRAP Score 12.1555

Importance

Changes 0
Metric Value
cc 12
eloc 33
nc 50
nop 3
dl 21
loc 59
rs 6.4485
c 0
b 0
f 0
ccs 35
cts 39
cp 0.8974
crap 12.1555

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * This file is part of SwiftMailer.
5
 * (c) 2004-2009 Chris Corbyn
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
/**
12
 * Handles Quoted Printable (QP) Encoding in Swift Mailer.
13
 *
14
 * Possibly the most accurate RFC 2045 QP implementation found in PHP.
15
 *
16
 * @author Chris Corbyn
17
 */
18
class Swift_Encoder_QpEncoder implements Swift_Encoder
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
19
{
20
    /**
21
     * The CharacterStream used for reading characters (as opposed to bytes).
22
     *
23
     * @var Swift_CharacterStream
24
     */
25
    protected $_charStream;
26
27
    /**
28
     * A filter used if input should be canonicalized.
29
     *
30
     * @var Swift_StreamFilter
31
     */
32
    protected $_filter;
33
34
    /**
35
     * Pre-computed QP for HUGE optimization.
36
     *
37
     * @var string[]
38
     */
39
    protected static $_qpMap = array(
40
        0 => '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04',
41
        5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09',
42
        10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E',
43
        15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13',
44
        20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18',
45
        25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D',
46
        30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22',
47
        35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27',
48
        40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C',
49
        45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31',
50
        50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36',
51
        55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B',
52
        60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40',
53
        65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45',
54
        70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A',
55
        75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F',
56
        80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54',
57
        85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59',
58
        90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E',
59
        95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63',
60
        100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68',
61
        105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D',
62
        110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72',
63
        115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77',
64
        120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C',
65
        125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81',
66
        130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86',
67
        135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B',
68
        140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90',
69
        145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95',
70
        150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A',
71
        155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F',
72
        160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4',
73
        165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9',
74
        170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE',
75
        175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3',
76
        180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8',
77
        185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD',
78
        190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2',
79
        195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7',
80
        200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC',
81
        205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1',
82
        210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6',
83
        215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB',
84
        220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0',
85
        225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5',
86
        230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA',
87
        235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF',
88
        240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4',
89
        245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9',
90
        250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE',
91
        255 => '=FF',
92
    );
93
94
    protected static $_safeMapShare = array();
95
96
    /**
97
     * A map of non-encoded ascii characters.
98
     *
99
     * @var string[]
100
     */
101
    protected $_safeMap = array();
102
103
    /**
104
     * Creates a new QpEncoder for the given CharacterStream.
105
     *
106
     * @param Swift_CharacterStream $charStream to use for reading characters
107
     * @param Swift_StreamFilter    $filter     if input should be canonicalized
108
     */
109 231
    public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null)
110
    {
111 231
        $this->_charStream = $charStream;
112
113 231 View Code Duplication
        if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
114 3
            $this->initSafeMap();
115 3
            self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap;
116 3
        } else {
117 231
            $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()];
118
        }
119
120 231
        $this->_filter = $filter;
121 231
    }
122
123
    /**
124
     * @return array
125
     */
126
    public function __sleep()
127
    {
128
        return array('_charStream', '_filter');
129
    }
130
131
    public function __wakeup()
132
    {
133 View Code Duplication
        if (!isset(self::$_safeMapShare[$this->getSafeMapShareId()])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
134
            $this->initSafeMap();
135
            self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap;
136
        } else {
137
            $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()];
138
        }
139
    }
140
141
    /**
142
     * @return string
143
     */
144 210
    protected function getSafeMapShareId()
145
    {
146 210
        return get_class($this);
147
    }
148
149 3
    protected function initSafeMap()
150
    {
151
        foreach (
152 3
            array_merge(
153 3
                array(0x09, 0x20),
154 3
                range(0x21, 0x3C),
155 3
                range(0x3E, 0x7E)
156 3
            ) as $byte
157 3
        ) {
158 3
            $this->_safeMap[$byte] = chr($byte);
159 3
        }
160 3
    }
161
162
    /**
163
     * Takes an unencoded string and produces a QP encoded string from it.
164
     *
165
     * QP encoded strings have a maximum line length of 76 characters.
166
     * If the first line needs to be shorter, indicate the difference with
167
     * $firstLineOffset.
168
     *
169
     * @param string $string          to encode
170
     * @param int    $firstLineOffset , optional
171
     * @param int    $maxLineLength   ,   optional 0 indicates the default of 76 chars
172
     *
173
     * @return string
174
     */
175 113
    public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
176
    {
177 113
        if ($maxLineLength > 76 || $maxLineLength <= 0) {
178 82
            $maxLineLength = 76;
179 82
        }
180
181 113
        $thisLineLength = $maxLineLength - $firstLineOffset;
182
183 113
        $lines = array();
184 113
        $lNo = 0;
185 113
        $lines[$lNo] = '';
186 113
        $currentLine = &$lines[$lNo++];
187 113
        $size = $lineLen = 0;
188
189 113
        $this->_charStream->flushContents();
190 113
        $this->_charStream->importString($string);
191
192 113
        while (false !== $bytes = $this->_nextSequence()) {
193
            // if we're filtering the input
194 113 View Code Duplication
            if (isset($this->_filter)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
195
                // if we can't filter because we need more bytes
196 66
                while ($this->_filter->shouldBuffer($bytes)) {
197
                    // then collect bytes into the buffer
198 2
                    if (false === $moreBytes = $this->_nextSequence(1)) {
199 2
                        break;
200
                    }
201
202
                    foreach ($moreBytes as $b) {
203
                        $bytes[] = $b;
204
                    }
205
                }
206
                // and filter them
207 66
                $bytes = $this->_filter->filter($bytes);
208 66
            }
209
210 113
            $enc = $this->_encodeByteSequence($bytes, $size);
211
212 113
            $i = strpos($enc, '=0D=0A');
213 113
            $newLineLength = $lineLen + ($i === false ? $size : $i);
214
215 113
            if ($currentLine && $newLineLength >= $thisLineLength) {
216 7
                $lines[$lNo] = '';
217 7
                $currentLine = &$lines[$lNo++];
218 7
                $thisLineLength = $maxLineLength;
219 7
                $lineLen = 0;
220 7
            }
221
222 113
            $currentLine .= $enc;
223
224 113 View Code Duplication
            if ($i === false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225 99
                $lineLen += $size;
226 99
            } else {
227
                // 6 is the length of '=0D=0A'.
228 21
                $lineLen = $size - strrpos($enc, '=0D=0A') - 6;
229
            }
230 113
        }
231
232 113
        return $this->_standardize(implode("=\r\n", $lines));
233
    }
234
235
    /**
236
     * Updates the charset used.
237
     *
238
     * @param string $charset
239
     */
240 193
    public function charsetChanged($charset)
241
    {
242 193
        $this->_charStream->setCharacterSet($charset);
243 193
    }
244
245
    /**
246
     * Encode the given byte array into a verbatim QP form.
247
     *
248
     * @param int[] $bytes
249
     * @param int   $size
250
     *
251
     * @return string
252
     */
253 126
    protected function _encodeByteSequence(array $bytes, &$size)
254
    {
255 126
        $ret = '';
256 126
        $size = 0;
257 126
        foreach ($bytes as $b) {
258
259 126
            $tmpRet = '';
260 126
            if (isset($this->_safeMap[$b])) {
261 91
                $tmpRet .= $this->_safeMap[$b];
262 91
                ++$size;
263 91
            } else {
264 83
                $tmpRet .= self::$_qpMap[$b];
265 83
                $size += 3;
266
            }
267
268 126
            if ($tmpRet !== '=00') {
269 126
              $ret .= $tmpRet;
270 126
            }
271 126
        }
272
273 126
        return $ret;
274
    }
275
276
    /**
277
     * Get the next sequence of bytes to read from the char stream.
278
     *
279
     * Fetching more than 4 chars at one is slower, as is fetching fewer bytes
280
     * Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6
281
     * bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes.
282
     *
283
     * @param int $size number of bytes to read
284
     *
285
     * @return int[]
286
     */
287 126
    protected function _nextSequence($size = 72)
288
    {
289 126
        return $this->_charStream->readBytes($size);
290
    }
291
292
    /**
293
     * Make sure CRLF is correct and HT/SPACE are in valid places.
294
     *
295
     * @param string $string
296
     *
297
     * @return string
298
     */
299 126
    protected function _standardize($string)
300
    {
301 126
        $string = str_replace(
302 126
            array("\t=0D=0A", ' =0D=0A', '=0D=0A'),
303 126
            array("=09\r\n", "=20\r\n", "\r\n"),
304
            $string
305 126
        );
306
307 126
        switch ($end = ord(substr($string, -1))) {
308 126
            case 0x09:
309 126
            case 0x20:
310 6
                $string = substr_replace($string, self::$_qpMap[$end], -1);
311 126
        }
312
313 126
        return $string;
314
    }
315
316
    /**
317
     * Make a deep copy of object.
318
     */
319 5
    public function __clone()
320
    {
321 5
        $this->_charStream = clone $this->_charStream;
322 5
    }
323
}
324