Completed
Push — 5.x ( 0b26a1...a498b0 )
by Lars
53:47 queued 44:42
created

Swift_Mime_Headers_AbstractHeader::createPhrase()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 4.0058
Metric Value
dl 0
loc 25
ccs 13
cts 14
cp 0.9286
rs 8.5806
cc 4
eloc 13
nc 4
nop 5
crap 4.0058
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
 * An abstract base MIME Header.
13
 *
14
 * @author Chris Corbyn
15
 */
16
abstract class Swift_Mime_Headers_AbstractHeader implements Swift_Mime_Header
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...
17
{
18
    const PHRASE_PATTERN = '(?:(?:(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]+(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?)|(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?"((?:(?:[ \t]*(?:\r\n))?[ \t])?(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21\x23-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])))*(?:(?:[ \t]*(?:\r\n))?[ \t])?"(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?))+?)';
19
20
    /**
21
     * The name of this Header.
22
     *
23
     * @var string
24
     */
25
    private $_name;
26
27
    /**
28
     * The Encoder used to encode this Header.
29
     *
30
     * @var Swift_Encoder|Swift_Mime_ContentEncoder
31
     */
32
    private $_encoder;
33
34
    /**
35
     * The maximum length of a line in the header.
36
     *
37
     * @var int
38
     */
39
    private $_lineLength = 78;
40
41
    /**
42
     * The language used in this Header.
43
     *
44
     * @var string
45
     */
46
    private $_lang;
47
48
    /**
49
     * The character set of the text in this Header.
50
     *
51
     * @var string
52
     */
53
    private $_charset = 'utf-8';
54
55
    /**
56
     * The value of this Header, cached.
57
     *
58
     * @var string
59
     */
60
    private $_cachedValue = null;
61
62
    /**
63
     * Creates a new Header.
64
     */
65 250
    public function __construct()
66
    {
67 250
    }
68
69
    /**
70
     * Set the character set used in this Header.
71
     *
72
     * @param string $charset
73
     */
74 185
    public function setCharset($charset)
75
    {
76 185
        $this->clearCachedValueIf($charset != $this->_charset);
77 185
        $this->_charset = $charset;
78 185
        if (isset($this->_encoder)) {
79 185
            $this->_encoder->charsetChanged($charset);
80 185
        }
81 185
    }
82
83
    /**
84
     * Get the character set used in this Header.
85
     *
86
     * @return string
87
     */
88 83
    public function getCharset()
89
    {
90 83
        return $this->_charset;
91
    }
92
93
    /**
94
     * Set the language used in this Header.
95
     *
96
     * For example, for US English, 'en-us'.
97
     * This can be unspecified.
98
     *
99
     * @param string $lang
100
     */
101 4
    public function setLanguage($lang)
102
    {
103 4
        $this->clearCachedValueIf($this->_lang != $lang);
104 4
        $this->_lang = $lang;
105 4
    }
106
107
    /**
108
     * Get the language used in this Header.
109
     *
110
     * @return string
111
     */
112 7
    public function getLanguage()
113
    {
114 7
        return $this->_lang;
115
    }
116
117
    /**
118
     * Set the encoder used for encoding the header.
119
     *
120
     * @param Swift_Mime_HeaderEncoder $encoder
121
     */
122 210
    public function setEncoder(Swift_Mime_HeaderEncoder $encoder)
123
    {
124 210
        $this->_encoder = $encoder;
125 210
        $this->_encoder->charsetChanged($this->_charset);
126
127 210
        $this->setCachedValue(null);
128 210
    }
129
130
    /**
131
     * Get the encoder used for encoding this Header.
132
     *
133
     * @return Swift_Mime_HeaderEncoder
134
     */
135 77
    public function getEncoder()
136
    {
137 77
        return $this->_encoder;
138
    }
139
140
    /**
141
     * Get the name of this header (e.g. charset).
142
     *
143
     * @return string
144
     */
145 152
    public function getFieldName()
146
    {
147 152
        return $this->_name;
148
    }
149
150
    /**
151
     * Set the maximum length of lines in the header (excluding EOL).
152
     *
153
     * @param int $lineLength
154
     */
155 20
    public function setMaxLineLength($lineLength)
156
    {
157 20
        $this->clearCachedValueIf($this->_lineLength != $lineLength);
158 20
        $this->_lineLength = $lineLength;
159 20
    }
160
161
    /**
162
     * Get the maximum permitted length of lines in this Header.
163
     *
164
     * @return int
165
     */
166 82
    public function getMaxLineLength()
167
    {
168 82
        return $this->_lineLength;
169
    }
170
171
    /**
172
     * Get this Header rendered as a RFC 2822 compliant string.
173
     *
174
     * @return string
175
     */
176 151
    public function toString()
177
    {
178 151
        return $this->_tokensToString($this->toTokens());
179
    }
180
181
    /**
182
     * Returns a string representation of this object.
183
     *
184
     * @return string
185
     *
186
     * @see toString()
187
     */
188
    public function __toString()
189
    {
190
        return $this->toString();
191
    }
192
193
    // -- Points of extension
194
195
    /**
196
     * Set the name of this Header field.
197
     *
198
     * @param string $name
199
     */
200 250
    protected function setFieldName($name)
201
    {
202 250
        $this->_name = $name;
203 250
    }
204
205
    /**
206
     * Produces a compliant, formatted RFC 2822 'phrase' based on the string given.
207
     *
208
     * @param Swift_Mime_Header        $header
209
     * @param string                   $string  as displayed
210
     * @param string                   $charset of the text
211
     * @param Swift_Mime_HeaderEncoder $encoder
212
     * @param bool                     $shorten the first line to make remove for header name
213
     *
214
     * @return string
215
     */
216 77
    protected function createPhrase(Swift_Mime_Header $header, $string, $charset, Swift_Mime_HeaderEncoder $encoder = null, $shorten = false)
0 ignored issues
show
Unused Code introduced by
The parameter $charset is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $encoder is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
217
    {
218
        // Treat token as exactly what was given
219 77
        $phraseStr = $string;
220
        // If it's not valid
221 77
        if (!preg_match('/^' . self::PHRASE_PATTERN . '$/D', $phraseStr)) {
222
            // .. but it is just ascii text, try escaping some characters
223
            // and make it a quoted-string
224 6
            if (preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $phraseStr)) {
225 3
                $phraseStr = $this->escapeSpecials($phraseStr, array('"'));
226 3
                $phraseStr = '"' . $phraseStr . '"';
227 3
            } else {
228
                // ... otherwise it needs encoding
229
                // Determine space remaining on line if first line
230 4
                if ($shorten) {
231 4
                    $usedLength = strlen($header->getFieldName() . ': ');
232 4
                } else {
233
                    $usedLength = 0;
234
                }
235 4
                $phraseStr = $this->encodeWords($header, $string, $usedLength);
236
            }
237 6
        }
238
239 77
        return $phraseStr;
240
    }
241
242
    /**
243
     * Escape special characters in a string (convert to quoted-pairs).
244
     *
245
     * @param string   $token
246
     * @param string[] $include additional chars to escape
247
     *
248
     * @return string
249
     */
250 3
    protected function escapeSpecials($token, array $include = array())
251
    {
252 3
        foreach (array_merge(array('\\'), $include) as $char) {
253 3
            $token = str_replace($char, '\\' . $char, $token);
254 3
        }
255
256 3
        return $token;
257
    }
258
259
    /**
260
     * Encode needed word tokens within a string of input.
261
     *
262
     * @param Swift_Mime_Header|Swift_Mime_Headers_AbstractHeader $header
263
     * @param string                                              $input
264
     * @param integer                                             $usedLength optional
265
     *
266
     * @return string
267
     */
268 155
    protected function encodeWords(Swift_Mime_Header $header, $input, $usedLength = -1)
269
    {
270 155
        $value = '';
271
272 155
        $tokens = $this->getEncodableWordTokens($input);
273
274 155
        foreach ($tokens as $token) {
275
            // See RFC 2822, Sect 2.2 (really 2.2 ?? TODO ?)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
276 155
            if ($this->tokenNeedsEncoding($token)) {
277
                // Don't encode starting WSP
278 16
                $firstChar = substr($token, 0, 1);
279
                switch ($firstChar) {
280 16
                    case ' ':
281 16
                    case "\t":
282 5
                        $value .= $firstChar;
283 5
                        $token = substr($token, 1);
284 5
                }
285
286 16
                if (-1 == $usedLength) {
287 13
                    $usedLength = strlen($header->getFieldName() . ': ') + strlen($value);
288 13
                }
289 16
                $value .= $this->getTokenAsEncodedWord($token, $usedLength);
290
291 16
                $header->setMaxLineLength(76); // Forcefully override
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Swift_Mime_Header as the method setMaxLineLength() does only exist in the following implementations of said interface: Swift_Mime_Headers_AbstractHeader, Swift_Mime_Headers_DateHeader, Swift_Mime_Headers_IdentificationHeader, Swift_Mime_Headers_MailboxHeader, Swift_Mime_Headers_ParameterizedHeader, Swift_Mime_Headers_PathHeader, Swift_Mime_Headers_UnstructuredHeader.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
292 16
            } else {
293 144
                $value .= $token;
294
            }
295 155
        }
296
297 155
        return $value;
298
    }
299
300
    /**
301
     * Test if a token needs to be encoded or not.
302
     *
303
     * @param string $token
304
     *
305
     * @return integer
306
     */
307 155
    protected function tokenNeedsEncoding($token)
308
    {
309 155
        if (preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token)) {
310 16
            return 1;
311
        } else {
312 144
            return 0;
313
        }
314
    }
315
316
    /**
317
     * Splits a string into tokens in blocks of words which can be encoded quickly.
318
     *
319
     * @param string $string
320
     *
321
     * @return string[]
322
     */
323 155
    protected function getEncodableWordTokens($string)
324
    {
325 155
        $tokens = array();
326
327 155
        $encodedToken = '';
328
        // Split at all whitespace boundaries
329 155
        foreach (preg_split('~(?=[\t ])~', $string) as $token) {
330 155
            if ($this->tokenNeedsEncoding($token)) {
331 16
                $encodedToken .= $token;
332 16
            } else {
333 144
                if ($encodedToken !== '') {
334 3
                    $tokens[] = $encodedToken;
335 3
                    $encodedToken = '';
336 3
                }
337 144
                $tokens[] = $token;
338
            }
339 155
        }
340
341 155
        if ($encodedToken !== '') {
342 16
            $tokens[] = $encodedToken;
343 16
        }
344
345 155
        return $tokens;
346
    }
347
348
    /**
349
     * Get a token as an encoded word for safe insertion into headers.
350
     *
351
     * @param string $token           token to encode
352
     * @param int    $firstLineOffset optional
353
     *
354
     * @return string
355
     */
356 17
    protected function getTokenAsEncodedWord($token, $firstLineOffset = 0)
357
    {
358
        // Adjust $firstLineOffset to account for space needed for syntax.
359
360 17
        $charsetDecl = $this->_charset;
361
362 17
        if (isset($this->_lang)) {
363 2
            $charsetDecl .= '*' . $this->_lang;
364 2
        }
365
366 17
        $encodingWrapperLength = strlen('=?' . $charsetDecl . '?' . $this->_encoder->getName() . '??=');
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Swift_Encoder as the method getName() does only exist in the following implementations of said interface: Swift_Mime_ContentEncoder_Base64ContentEncoder, Swift_Mime_ContentEncoder_NativeQpContentEncoder, Swift_Mime_ContentEncoder_PlainContentEncoder, Swift_Mime_ContentEncoder_QpContentEncoder, Swift_Mime_ContentEncoder_QpContentEncoderProxy, Swift_Mime_ContentEncoder_RawContentEncoder, Swift_Mime_HeaderEncoder_Base64HeaderEncoder, Swift_Mime_HeaderEncoder_QpHeaderEncoder.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
367
368 17
        if ($firstLineOffset >= 75) {
369
            // Does this logic need to be here?
370
            $firstLineOffset = 0;
371
        }
372
373
        /** @noinspection PhpMethodParametersCountMismatchInspection */
374 17
        $encodedTextLines = explode(
375 17
            "\r\n",
376 17
            $this->_encoder->encodeString($token, $firstLineOffset, 75 - $encodingWrapperLength, $this->_charset)
0 ignored issues
show
Unused Code introduced by
The call to Swift_Encoder::encodeString() has too many arguments starting with $this->_charset.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
377 17
        );
378
379 17
        if (Swift::strtolowerWithStaticCache($this->_charset) !== 'iso-2022-jp') {
380
            // special encoding for iso-2022-jp using mb_encode_mimeheader
381 17
            foreach ($encodedTextLines as $lineNum => $line) {
382 17
                $encodedTextLines[$lineNum] = '=?' . $charsetDecl . '?' . $this->_encoder->getName() . '?' . $line . '?=';
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Swift_Encoder as the method getName() does only exist in the following implementations of said interface: Swift_Mime_ContentEncoder_Base64ContentEncoder, Swift_Mime_ContentEncoder_NativeQpContentEncoder, Swift_Mime_ContentEncoder_PlainContentEncoder, Swift_Mime_ContentEncoder_QpContentEncoder, Swift_Mime_ContentEncoder_QpContentEncoderProxy, Swift_Mime_ContentEncoder_RawContentEncoder, Swift_Mime_HeaderEncoder_Base64HeaderEncoder, Swift_Mime_HeaderEncoder_QpHeaderEncoder.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
383 17
            }
384 17
        }
385
386 17
        return implode("\r\n ", $encodedTextLines);
387
    }
388
389
    /**
390
     * Generates tokens from the given string which include CRLF as individual tokens.
391
     *
392
     * @param string $token
393
     *
394
     * @return string[]
395
     */
396 151
    protected function generateTokenLines($token)
397
    {
398 151
        return preg_split('~(\r\n)~', $token, -1, PREG_SPLIT_DELIM_CAPTURE);
399
    }
400
401
    /**
402
     * Set a value into the cache.
403
     *
404
     * @param string $value
405
     */
406 239
    protected function setCachedValue($value)
407
    {
408 239
        $this->_cachedValue = $value;
409 239
    }
410
411
    /**
412
     * Get the value in the cache.
413
     *
414
     * @return string
415
     */
416 170
    protected function getCachedValue()
417
    {
418 170
        return $this->_cachedValue;
419
    }
420
421
    /**
422
     * Clear the cached value if $condition is met.
423
     *
424
     * @param bool $condition
425
     */
426 225
    protected function clearCachedValueIf($condition)
427
    {
428 225
        if ($condition) {
429 200
            $this->setCachedValue(null);
430 200
        }
431 225
    }
432
433
    /**
434
     * Generate a list of all tokens in the final header.
435
     *
436
     * @param string $string The string to tokenize
437
     *
438
     * @return array An array of tokens as strings
439
     */
440 151
    protected function toTokens($string = null)
441
    {
442 151
        if (null === $string) {
443 146
            $string = $this->getFieldBody();
444 146
        }
445
446 151
        $tokens = array();
447
448
        // Generate atoms; split at all invisible boundaries followed by WSP
449 151
        foreach (preg_split('~(?=[ \t])~', $string) as $token) {
450 151
            $newTokens = $this->generateTokenLines($token);
451 151
            foreach ($newTokens as $newToken) {
452 151
                $tokens[] = $newToken;
453 151
            }
454 151
        }
455
456 151
        return $tokens;
457
    }
458
459
    /**
460
     * Takes an array of tokens which appear in the header and turns them into
461
     * an RFC 2822 compliant string, adding FWSP where needed.
462
     *
463
     * @param string[] $tokens
464
     *
465
     * @return string
466
     */
467 151
    private function _tokensToString(array $tokens)
468
    {
469 151
        $lineCount = 0;
470 151
        $headerLines = array();
471 151
        $headerLines[] = $this->_name . ': ';
472 151
        $currentLine = &$headerLines[$lineCount++];
473
474
        // Build all tokens back into compliant header
475 151
        foreach ($tokens as $i => $token) {
476
            // Line longer than specified maximum or token was just a new line
477
            if (
478 151
                ("\r\n" == $token)
479
                ||
480 151
                ($i > 0 && strlen($currentLine . $token) > $this->_lineLength)
481 151
                &&
482 41
                0 < strlen($currentLine)
483 151
            ) {
484 42
                $headerLines[] = '';
485 42
                $currentLine = &$headerLines[$lineCount++];
486 42
            }
487
488
            // Append token to the line
489 151
            if ("\r\n" != $token) {
490 151
                $currentLine .= $token;
491 151
            }
492 151
        }
493
494
        // Implode with FWS (RFC 2822, 2.2.3)
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
495 151
        return implode("\r\n", $headerLines) . "\r\n";
496
    }
497
498
    /**
499
     * Make a deep copy of object
500
     */
501 5
    public function __clone()
502
    {
503 5
        if ($this->_encoder !== null) {
504 5
            $this->_encoder = clone $this->_encoder;
505 5
        }
506 5
    }
507
}
508