Completed
Push — 5.x ( 50a512...e1df8b )
by Lars
05:40
created

Swift_Encoder_QpEncoder   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 291
Duplicated Lines 11.34 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 86.75%
Metric Value
wmc 30
lcom 1
cbo 3
dl 33
loc 291
ccs 72
cts 83
cp 0.8675
rs 10

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 6 11 2
A __sleep() 0 4 1
A __wakeup() 6 9 2
A getSafeMapShareId() 0 4 1
A initSafeMap() 0 7 2
C encodeString() 21 59 12
A charsetChanged() 0 4 1
A _encodeByteSequence() 0 16 3
A _nextSequence() 0 4 1
A _standardize() 0 16 3
A __clone() 0 6 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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 189
    public function __construct(Swift_CharacterStream $charStream, Swift_StreamFilter $filter = null)
110
    {
111 189
        $this->_charStream = $charStream;
112 189 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...
113 3
            $this->initSafeMap();
114 3
            self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap;
115
        } else {
116 189
            $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()];
117
        }
118 189
        $this->_filter = $filter;
119 189
    }
120
121
    public function __sleep()
122
    {
123
        return array('_charStream', '_filter');
124
    }
125
126
    public function __wakeup()
127
    {
128 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...
129
            $this->initSafeMap();
130
            self::$_safeMapShare[$this->getSafeMapShareId()] = $this->_safeMap;
131
        } else {
132
            $this->_safeMap = self::$_safeMapShare[$this->getSafeMapShareId()];
133
        }
134
    }
135
136 168
    protected function getSafeMapShareId()
137
    {
138 168
        return get_class($this);
139
    }
140
141 3
    protected function initSafeMap()
142
    {
143 3
        foreach (array_merge(
144 3
            array(0x09, 0x20), range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) {
145 3
            $this->_safeMap[$byte] = chr($byte);
146
        }
147 3
    }
148
149
    /**
150
     * Takes an unencoded string and produces a QP encoded string from it.
151
     *
152
     * QP encoded strings have a maximum line length of 76 characters.
153
     * If the first line needs to be shorter, indicate the difference with
154
     * $firstLineOffset.
155
     *
156
     * @param string $string           to encode
157
     * @param int    $firstLineOffset, optional
0 ignored issues
show
Documentation introduced by
There is no parameter named $firstLineOffset,. Did you maybe mean $firstLineOffset?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
158
     * @param int    $maxLineLength,   optional 0 indicates the default of 76 chars
0 ignored issues
show
Documentation introduced by
There is no parameter named $maxLineLength,. Did you maybe mean $maxLineLength?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function. It has, however, found a similar but not annotated parameter which might be a good fit.

Consider the following example. The parameter $ireland is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $ireland
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was changed, but the annotation was not.

Loading history...
159
     *
160
     * @return string
161
     */
162 72
    public function encodeString($string, $firstLineOffset = 0, $maxLineLength = 0)
163
    {
164 72
        if ($maxLineLength > 76 || $maxLineLength <= 0) {
165 70
            $maxLineLength = 76;
166
        }
167
168 72
        $thisLineLength = $maxLineLength - $firstLineOffset;
169
170 72
        $lines = array();
171 72
        $lNo = 0;
172 72
        $lines[$lNo] = '';
173 72
        $currentLine = &$lines[$lNo++];
174 72
        $size = $lineLen = 0;
175
176 72
        $this->_charStream->flushContents();
177 72
        $this->_charStream->importString($string);
178
179 72
        while (false !== $bytes = $this->_nextSequence()) {
180
            // if we're filtering the input
181 72 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...
182
                // if we can't filter because we need more bytes
183 54
                while ($this->_filter->shouldBuffer($bytes)) {
184
                    // then collect bytes into the buffer
185 2
                    if (false === $moreBytes = $this->_nextSequence(1)) {
186 2
                        break;
187
                    }
188
189
                    foreach ($moreBytes as $b) {
190
                        $bytes[] = $b;
191
                    }
192
                }
193
                // and filter them
194 54
                $bytes = $this->_filter->filter($bytes);
195
            }
196
197 72
            $enc = $this->_encodeByteSequence($bytes, $size);
198
199 72
            $i = strpos($enc, '=0D=0A');
200 72
            $newLineLength = $lineLen + ($i === false ? $size : $i);
201
202 72
            if ($currentLine && $newLineLength >= $thisLineLength) {
203 5
                $lines[$lNo] = '';
204 5
                $currentLine = &$lines[$lNo++];
205 5
                $thisLineLength = $maxLineLength;
206 5
                $lineLen = 0;
207
            }
208
209 72
            $currentLine .= $enc;
210
211 72 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...
212 55
                $lineLen += $size;
213
            } else {
214
                // 6 is the length of '=0D=0A'.
215 18
                $lineLen = $size - strrpos($enc, '=0D=0A') - 6;
216
            }
217
        }
218
219 72
        return $this->_standardize(implode("=\r\n", $lines));
220
    }
221
222
    /**
223
     * Updates the charset used.
224
     *
225
     * @param string $charset
226
     */
227 151
    public function charsetChanged($charset)
228
    {
229 151
        $this->_charStream->setCharacterSet($charset);
230 151
    }
231
232
    /**
233
     * Encode the given byte array into a verbatim QP form.
234
     *
235
     * @param integer[] $bytes
236
     * @param int       $size
237
     *
238
     * @return string
239
     */
240 85
    protected function _encodeByteSequence(array $bytes, &$size)
241
    {
242 85
        $ret = '';
243 85
        $size = 0;
244 85
        foreach ($bytes as $b) {
245 85
            if (isset($this->_safeMap[$b])) {
246 79
                $ret .= $this->_safeMap[$b];
247 79
                ++$size;
248
            } else {
249 42
                $ret .= self::$_qpMap[$b];
250 85
                $size += 3;
251
            }
252
        }
253
254 85
        return $ret;
255
    }
256
257
    /**
258
     * Get the next sequence of bytes to read from the char stream.
259
     *
260
     * Fetching more than 4 chars at one is slower, as is fetching fewer bytes
261
     * Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6
262
     * bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes.
263
     *
264
     * TODO: need testing
265
     *
266
     * @param int $size number of bytes to read
267
     *
268
     * @return integer[]
269
     */
270 85
    protected function _nextSequence($size = 1024)
271
    {
272 85
        return $this->_charStream->readBytes($size);
273
    }
274
275
    /**
276
     * Make sure CRLF is correct and HT/SPACE are in valid places.
277
     *
278
     * @param string $string
279
     *
280
     * @return string
281
     */
282 85
    protected function _standardize($string)
283
    {
284 85
        $string = str_replace(
285 85
            array("\t=0D=0A", ' =0D=0A', '=0D=0A'),
286 85
            array("=09\r\n", "=20\r\n", "\r\n"),
287
            $string
288
        );
289
290 85
        switch ($end = ord(substr($string, -1))) {
291
            case 0x09:
292 85
            case 0x20:
293 6
                $string = substr_replace($string, self::$_qpMap[$end], -1);
294
        }
295
296 85
        return $string;
297
    }
298
299
    /**
300
     * Make a deep copy of object.
301
     */
302 2
    public function __clone()
303
    {
304 2
        if (true === Swift::$useMemorySpool) {
305 2
            $this->_charStream = clone $this->_charStream;
306
        }
307 2
    }
308
}
309