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

Swift_Mime_SimpleMimeEntity::__clone()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 25
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5
Metric Value
dl 0
loc 25
ccs 12
cts 12
cp 1
rs 8.439
cc 5
eloc 14
nc 4
nop 0
crap 5
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
 * A MIME entity, in a multipart message.
13
 *
14
 * @author Chris Corbyn
15
 */
16
class Swift_Mime_SimpleMimeEntity implements Swift_Mime_MimeEntity
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
    /**
19
     * A collection of Headers for this mime entity
20
     *
21
     * @var Swift_Mime_HeaderSet
22
     */
23
    private $_headers;
24
25
    /**
26
     * The body as a string, or a stream
27
     */
28
    private $_body;
29
30
    /**
31
     * The encoder that encodes the body into a streamable format
32
     *
33
     * @var Swift_Mime_ContentEncoder
34
     */
35
    private $_encoder;
36
37
    /**
38
     * The validator for email
39
     *
40
     * @var Swift_EmailValidatorBridge
41
     */
42
    private $_emailValidator;
43
44
    /**
45
     * A mime boundary, if any is used
46
     */
47
    private $_boundary;
48
49
    /**
50
     * Mime types to be used based on the nesting level
51
     *
52
     * @var array
53
     */
54
    private $_compositeRanges = array(
55
        'multipart/mixed' => array(self::LEVEL_TOP, self::LEVEL_MIXED),
56
        'multipart/alternative' => array(self::LEVEL_MIXED, self::LEVEL_ALTERNATIVE),
57
        'multipart/related' => array(self::LEVEL_ALTERNATIVE, self::LEVEL_RELATED),
58
    );
59
60
    /**
61
     * A set of filter rules to define what level an entity should be nested at
62
     *
63
     * @var array
64
     */
65
    private $_compoundLevelFilters = array();
66
67
    /**
68
     * The nesting level of this entity
69
     *
70
     * @var int
71
     */
72
    private $_nestingLevel = self::LEVEL_ALTERNATIVE;
73
74
    /**
75
     * A KeyCache instance used during encoding and streaming
76
     *
77
     * @var Swift_KeyCache
78
     */
79
    private $_cache;
80
81
    /**
82
     * Direct descendants of this entity
83
     *
84
     * @var Swift_Mime_MimeEntity[]|array
85
     */
86
    private $_immediateChildren = array();
87
88
    /**
89
     * All descendants of this entity
90
     *
91
     * @var Swift_Mime_MimeEntity[]
92
     */
93
    private $_children = array();
94
95
    /**
96
     * The maximum line length of the body of this entity
97
     *
98
     * @var int
99
     */
100
    private $_maxLineLength = 78;
101
102
    /**
103
     * The order in which alternative mime types should appear
104
     *
105
     * @var array
106
     */
107
    private $_alternativePartOrder = array(
108
        'text/plain' => 1,
109
        'text/html' => 2,
110
        'multipart/related' => 3,
111
    );
112
113
    /**
114
     * The CID of this entity
115
     *
116
     * @var string
117
     */
118
    private $_id;
119
120
    /**
121
     * The key used for accessing the cache
122
     *
123
     * @var string
124
     */
125
    private $_cacheKey;
126
127
    protected $_userContentType;
128
129
    /**
130
     * Create a new SimpleMimeEntity with $headers, $encoder and $cache.
131
     *
132
     * @param Swift_Mime_HeaderSet       $headers
133
     * @param Swift_Mime_ContentEncoder  $encoder
134
     * @param Swift_KeyCache             $cache
135
     * @param Swift_EmailValidatorBridge $emailValidator
136
     */
137 497
    public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_EmailValidatorBridge $emailValidator)
138
    {
139 497
        $this->_cacheKey = $this->_generateNewCacheKey();
140 497
        $this->_cache = $cache;
141 497
        $this->_headers = $headers;
142 497
        $this->_emailValidator = $emailValidator;
143 497
        $this->setEncoder($encoder);
144 497
        $this->_headers->defineOrdering(array('Content-Type', 'Content-Transfer-Encoding'));
145
146
        // This array specifies that, when the entire MIME document contains
147
        // $compoundLevel, then for each child within $level, if its Content-Type
148
        // is $contentType then it should be treated as if it's level is
149
        // $neededLevel instead.  I tried to write that unambiguously! :-\
150
        // Data Structure:
151
        // array (
152
        //   $compoundLevel => array(
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
153
        //     $level => array(
0 ignored issues
show
Unused Code Comprehensibility introduced by
43% 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...
154
        //       $contentType => $neededLevel
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
155
        //     )
156
        //   )
157
        // )
158
159 497
        $this->_compoundLevelFilters = array(
160 497
            (self::LEVEL_ALTERNATIVE + self::LEVEL_RELATED) => array(
161 497
                self::LEVEL_ALTERNATIVE => array(
162 497
                    'text/plain' => self::LEVEL_ALTERNATIVE,
163 497
                    'text/html' => self::LEVEL_RELATED,
164
                ),
165
            ),
166
        );
167
168 497
        $this->_id = $this->getRandomId();
169 497
    }
170
171
    /**
172
     * Generate a new Content-ID or Message-ID for this MIME entity.
173
     *
174
     * @return string
175
     */
176 15
    public function generateId()
177
    {
178 15
        $this->setId($this->getRandomId());
179
180 15
        return $this->_id;
181
    }
182
183
    /**
184
     * Get the {@link Swift_Mime_HeaderSet} for this entity.
185
     *
186
     * @return Swift_Mime_HeaderSet
187
     */
188 360
    public function getHeaders()
189
    {
190 360
        return $this->_headers;
191
    }
192
193
    /**
194
     * Get the nesting level of this entity.
195
     *
196
     * @see LEVEL_TOP, LEVEL_MIXED, LEVEL_RELATED, LEVEL_ALTERNATIVE
197
     *
198
     * @return int
199
     */
200 20
    public function getNestingLevel()
201
    {
202 20
        return $this->_nestingLevel;
203
    }
204
205
    /**
206
     * Get the Content-type of this entity.
207
     *
208
     * @return string
209
     */
210 53
    public function getContentType()
211
    {
212 53
        return $this->_getHeaderFieldModel('Content-Type');
213
    }
214
215
    /**
216
     * Set the Content-type of this entity.
217
     *
218
     * @param string $type
219
     *
220
     * @return Swift_Mime_SimpleMimeEntity
221
     */
222 452
    public function setContentType($type)
223
    {
224 452
        $this->_setContentTypeInHeaders($type);
225
        // Keep track of the value so that if the content-type changes automatically
226
        // due to added child entities, it can be restored if they are later removed
227 452
        $this->_userContentType = $type;
228
229 452
        return $this;
230
    }
231
232
    /**
233
     * Get the CID of this entity.
234
     *
235
     * The CID will only be present in headers if a Content-ID header is present.
236
     *
237
     * @return string
238
     */
239 307
    public function getId()
240
    {
241 307
        $tmp = (array) $this->_getHeaderFieldModel($this->_getIdField());
242
243 307
        return $this->_headers->has($this->_getIdField()) ? current($tmp) : $this->_id;
244
    }
245
246
    /**
247
     * Set the CID of this entity.
248
     *
249
     * @param string $id
250
     *
251
     * @return Swift_Mime_SimpleMimeEntity
252
     */
253 310
    public function setId($id)
254
    {
255 310
        if (!$this->_setHeaderFieldModel($this->_getIdField(), $id)) {
256 303
            $this->_headers->addIdHeader($this->_getIdField(), $id);
257
        }
258 310
        $this->_id = $id;
259
260 310
        return $this;
261
    }
262
263
    /**
264
     * Get the description of this entity.
265
     *
266
     * This value comes from the Content-Description header if set.
267
     *
268
     * @return string
269
     */
270 5
    public function getDescription()
271
    {
272 5
        return $this->_getHeaderFieldModel('Content-Description');
273
    }
274
275
    /**
276
     * Set the description of this entity.
277
     *
278
     * This method sets a value in the Content-ID header.
279
     *
280
     * @param string $description
281
     *
282
     * @return Swift_Mime_SimpleMimeEntity
283
     */
284 15
    public function setDescription($description)
285
    {
286 15
        if (!$this->_setHeaderFieldModel('Content-Description', $description)) {
287 10
            $this->_headers->addTextHeader('Content-Description', $description);
288
        }
289
290 15
        return $this;
291
    }
292
293
    /**
294
     * Get the maximum line length of the body of this entity.
295
     *
296
     * @return int
297
     */
298 114
    public function getMaxLineLength()
299
    {
300 114
        return $this->_maxLineLength;
301
    }
302
303
    /**
304
     * Set the maximum line length of lines in this body.
305
     *
306
     * Though not enforced by the library, lines should not exceed 1000 chars.
307
     *
308
     * @param int $length
309
     *
310
     * @return Swift_Mime_SimpleMimeEntity
311
     */
312 32
    public function setMaxLineLength($length)
313
    {
314 32
        $this->_maxLineLength = $length;
315
316 32
        return $this;
317
    }
318
319
    /**
320
     * Get all children added to this entity.
321
     *
322
     * @return Swift_Mime_MimeEntity[]
323
     */
324 145
    public function getChildren()
325
    {
326 145
        return $this->_children;
327
    }
328
329
    /**
330
     * Set all children of this entity.
331
     *
332
     * @param Swift_Mime_MimeEntity[] $children
333
     * @param int                     $compoundLevel For internal use only
334
     *
335
     * @return Swift_Mime_SimpleMimeEntity
336
     */
337 115
    public function setChildren(array $children, $compoundLevel = null)
338
    {
339
        // TODO: Try to refactor this logic
340
341 115
        $compoundLevel = isset($compoundLevel) ? $compoundLevel : $this->_getCompoundLevel($children);
342 115
        $immediateChildren = array();
343 115
        $grandchildren = array();
344 115
        $newContentType = $this->_userContentType;
345
346 115
        foreach ($children as $child) {
347 103
            $level = $this->_getNeededChildLevel($child, $compoundLevel);
348 103
            if (empty($immediateChildren)) {
349
                //first iteration
350 103
                $immediateChildren = array($child);
351
            } else {
352 56
                $nextLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel);
353 56
                if ($nextLevel == $level) {
354 40
                    $immediateChildren[] = $child;
355 20
                } elseif ($level < $nextLevel) {
356
                    // Re-assign immediateChildren to grandchildren
357 13
                    $grandchildren = array_merge($grandchildren, $immediateChildren);
358
                    // Set new children
359 13
                    $immediateChildren = array($child);
360
                } else {
361 103
                    $grandchildren[] = $child;
362
                }
363
            }
364
        }
365
366 115
        if ($immediateChildren) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $immediateChildren of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
367 103
            $lowestLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel);
368
369
            // Determine which composite media type is needed to accommodate the
370
            // immediate children
371 103
            foreach ($this->_compositeRanges as $mediaType => $range) {
372 103
                if ($lowestLevel > $range[0] && $lowestLevel <= $range[1]) {
373 77
                    $newContentType = $mediaType;
374
375 103
                    break;
376
                }
377
            }
378
379
            // Put any grandchildren in a subpart
380 103
            if (!empty($grandchildren)) {
381 20
                $subentity = $this->_createChild();
382 20
                $subentity->_setNestingLevel($lowestLevel);
383 20
                $subentity->setChildren($grandchildren, $compoundLevel);
384 20
                array_unshift($immediateChildren, $subentity);
385
            }
386
        }
387
388 115
        $this->_immediateChildren = $immediateChildren;
389 115
        $this->_children = $children;
390 115
        $this->_setContentTypeInHeaders($newContentType);
391 115
        $this->_fixHeaders();
392 115
        $this->_sortChildren();
393
394 115
        return $this;
395
    }
396
397
    /**
398
     * Get the body of this entity as a string.
399
     *
400
     * @return string
401
     */
402 125
    public function getBody()
403
    {
404 125
        return $this->_body instanceof Swift_OutputByteStream ? $this->_readStream($this->_body) : $this->_body;
405
    }
406
407
    /**
408
     * Set the body of this entity, either as a string, or as an instance of
409
     * {@link Swift_OutputByteStream}.
410
     *
411
     * @param mixed  $body
412
     * @param string $contentType optional
413
     *
414
     * @return Swift_Mime_SimpleMimeEntity
415
     */
416 218
    public function setBody($body, $contentType = null)
417
    {
418 218
        if ($body !== $this->_body) {
419 157
            $this->_clearCache();
420
        }
421
422 218
        $this->_body = $body;
423 218
        if (isset($contentType)) {
424 17
            $this->setContentType($contentType);
425
        }
426
427 218
        return $this;
428
    }
429
430
    /**
431
     * Get the encoder used for the body of this entity.
432
     *
433
     * @return Swift_Mime_ContentEncoder
434
     */
435 27
    public function getEncoder()
436
    {
437 27
        return $this->_encoder;
438
    }
439
440
    /**
441
     * Set the encoder used for the body of this entity.
442
     *
443
     * @param Swift_Mime_ContentEncoder $encoder
444
     *
445
     * @return Swift_Mime_SimpleMimeEntity
446
     */
447 497
    public function setEncoder(Swift_Mime_ContentEncoder $encoder)
448
    {
449 497
        if ($encoder !== $this->_encoder) {
450 497
            $this->_clearCache();
451
        }
452
453 497
        $this->_encoder = $encoder;
454 497
        $this->_setEncoding($encoder->getName());
455 497
        $this->_notifyEncoderChanged($encoder);
456
457 497
        return $this;
458
    }
459
460
    /**
461
     * Get the boundary used to separate children in this entity.
462
     *
463
     * @return string
464
     */
465 118
    public function getBoundary()
466
    {
467 118
        if (!isset($this->_boundary)) {
468 102
            $this->_boundary = '_=_swift_v5_' . time() . '_' . md5(getmypid() . mt_rand() . uniqid('', true)) . '_=_';
469
        }
470
471 118
        return $this->_boundary;
472
    }
473
474
    /**
475
     * Set the boundary used to separate children in this entity.
476
     *
477
     * @param string $boundary
478
     *
479
     * @throws Swift_RfcComplianceException
480
     *
481
     * @return Swift_Mime_SimpleMimeEntity
482
     */
483 29
    public function setBoundary($boundary)
484
    {
485 29
        $this->_assertValidBoundary($boundary);
486 29
        $this->_boundary = $boundary;
487
488 29
        return $this;
489
    }
490
491
    /**
492
     * Receive notification that the charset of this entity, or a parent entity
493
     * has changed.
494
     *
495
     * @param string $charset
496
     */
497 140
    public function charsetChanged($charset)
498
    {
499 140
        $this->_notifyCharsetChanged($charset);
500 140
    }
501
502
    /**
503
     * Receive notification that the encoder of this entity or a parent entity
504
     * has changed.
505
     *
506
     * @param Swift_Mime_ContentEncoder $encoder
507
     */
508 6
    public function encoderChanged(Swift_Mime_ContentEncoder $encoder)
509
    {
510 6
        $this->_notifyEncoderChanged($encoder);
511 6
    }
512
513
    /**
514
     * Get this entire entity as a string.
515
     *
516
     * @return string
517
     */
518 183
    public function toString()
519
    {
520 183
        $string = $this->_headers->toString();
521 183
        $string .= $this->_bodyToString();
522
523 183
        return $string;
524
    }
525
526
    /**
527
     * Get this entire entity as a string.
528
     *
529
     * @return string
530
     */
531 183
    protected function _bodyToString()
532
    {
533 183
        $string = '';
534
535 183
        if (isset($this->_body) && empty($this->_immediateChildren)) {
536 95
            if ($this->_cache->hasKey($this->_cacheKey, 'body')) {
537 6
                $body = $this->_cache->getString($this->_cacheKey, 'body');
538
            } else {
539 90
                $body = "\r\n" . $this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength());
540 90
                $this->_cache->setString($this->_cacheKey, 'body', $body, Swift_KeyCache::MODE_WRITE);
541
            }
542 95
            $string .= $body;
543
        }
544
545 183
        if (!empty($this->_immediateChildren)) {
546 39
            foreach ($this->_immediateChildren as $child) {
547 39
                $string .= "\r\n\r\n--" . $this->getBoundary() . "\r\n";
548 39
                $string .= $child->toString();
549
            }
550 39
            $string .= "\r\n\r\n--" . $this->getBoundary() . "--\r\n";
551
        }
552
553 183
        return $string;
554
    }
555
556
    /**
557
     * Returns a string representation of this object.
558
     *
559
     * @see toString()
560
     *
561
     * @return string
562
     */
563
    public function __toString()
564
    {
565
        return $this->toString();
566
    }
567
568
    /**
569
     * Write this entire entity to a {@see Swift_InputByteStream}.
570
     *
571
     * @param Swift_InputByteStream
572
     */
573 37
    public function toByteStream(Swift_InputByteStream $is)
574
    {
575 37
        $is->write($this->_headers->toString());
576 37
        $is->commit();
577
578 37
        $this->_bodyToByteStream($is);
579 37
    }
580
581
    /**
582
     * Write this entire entity to a {@link Swift_InputByteStream}.
583
     *
584
     * @param Swift_InputByteStream
585
     */
586 37
    protected function _bodyToByteStream(Swift_InputByteStream $is)
587
    {
588 37
        if (empty($this->_immediateChildren)) {
589 37
            if (isset($this->_body)) {
590 27
                if ($this->_cache->hasKey($this->_cacheKey, 'body')) {
591 8
                    $this->_cache->exportToByteStream($this->_cacheKey, 'body', $is);
592
                } else {
593 22
                    $cacheIs = $this->_cache->getInputByteStream($this->_cacheKey, 'body');
594 22
                    if ($cacheIs) {
595 17
                        $is->bind($cacheIs);
596
                    }
597
598 22
                    $is->write("\r\n");
599
600 22
                    if ($this->_body instanceof Swift_OutputByteStream) {
601 14
                        $this->_body->setReadPointer(0);
602
603 14
                        $this->_encoder->encodeByteStream($this->_body, $is, 0, $this->getMaxLineLength());
604
                    } else {
605 22
                        $is->write($this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength()));
606
                    }
607
608 22
                    if ($cacheIs) {
609 17
                        $is->unbind($cacheIs);
610
                    }
611
                }
612
            }
613
        }
614
615 37
        if (!empty($this->_immediateChildren)) {
616 9
            foreach ($this->_immediateChildren as $child) {
617 9
                $is->write("\r\n\r\n--" . $this->getBoundary() . "\r\n");
618 9
                $child->toByteStream($is);
619
            }
620 9
            $is->write("\r\n\r\n--" . $this->getBoundary() . "--\r\n");
621
        }
622 37
    }
623
624
    /**
625
     * Get the name of the header that provides the ID of this entity.
626
     */
627 106
    protected function _getIdField()
628
    {
629 106
        return 'Content-ID';
630
    }
631
632
    /**
633
     * Get the model data (usually an array or a string) for $field.
634
     *
635
     * @param string $field
636
     *
637
     * @return string
638
     */
639 317
    protected function _getHeaderFieldModel($field)
640
    {
641 317
        if ($this->_headers->has($field)) {
642 152
            return $this->_headers->get($field)->getFieldBodyModel();
643
        } else {
644 300
            return null;
645
        }
646
    }
647
648
    /**
649
     * Set the model data for $field.
650
     *
651
     * @param string $field
652
     * @param $model
653
     *
654
     * @return bool
655
     */
656 497
    protected function _setHeaderFieldModel($field, $model)
657
    {
658 497
        if ($this->_headers->has($field)) {
659 247
            $this->_headers->get($field)->setFieldBodyModel($model);
660
661 247
            return true;
662
        }
663
664 496
        return false;
665
    }
666
667
    /**
668
     * Get the parameter value of $parameter on $field header.
669
     * @param string $field
670
     * @param string $parameter
671
     */
672 163
    protected function _getHeaderParameter($field, $parameter)
673
    {
674 163
        if ($this->_headers->has($field)) {
675 131
            return $this->_headers->get($field)->getParameter($parameter);
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 getParameter() does only exist in the following implementations of said interface: Swift_Mime_Headers_ParameterizedHeader.

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...
676
        }
677 32
    }
678
679
    /**
680
     * Set the parameter value of $parameter on $field header.
681
     * @param string $field
682
     * @param string $parameter
683
     * @param        $value
684
     *
685
     * @return bool
686
     */
687 238
    protected function _setHeaderParameter($field, $parameter, $value)
688
    {
689 238
        if ($this->_headers->has($field)) {
690 189
            $this->_headers->get($field)->setParameter($parameter, $value);
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 setParameter() does only exist in the following implementations of said interface: Swift_Mime_Headers_ParameterizedHeader.

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...
691
692 189
            return true;
693
        }
694
695 55
        return false;
696
    }
697
698
    /**
699
     * Re-evaluate what content type and encoding should be used on this entity.
700
     */
701 115
    protected function _fixHeaders()
702
    {
703 115
        if (count($this->_immediateChildren)) {
704 103
            $this->_setHeaderParameter('Content-Type', 'boundary',
705 103
                                       $this->getBoundary()
706
            );
707 103
            $this->_headers->remove('Content-Transfer-Encoding');
708
        } else {
709 21
            $this->_setHeaderParameter('Content-Type', 'boundary', null);
710 21
            $this->_setEncoding($this->_encoder->getName());
711
        }
712 115
    }
713
714
    /**
715
     * Get the KeyCache used in this entity.
716
     *
717
     * @return Swift_KeyCache
718
     */
719 17
    protected function _getCache()
720
    {
721 17
        return $this->_cache;
722
    }
723
724
    /**
725
     * Get the email-validator.
726
     *
727
     * @return Swift_EmailValidatorBridge
728
     */
729 17
    protected function _getEmailValidator()
730
    {
731 17
        return $this->_emailValidator;
732
    }
733
734
    /**
735
     * Empty the KeyCache for this entity.
736
     */
737 497
    protected function _clearCache()
738
    {
739 497
        $this->_cache->clearKey($this->_cacheKey, 'body');
740 497
    }
741
742
    /**
743
     * Returns a random Content-ID or Message-ID.
744
     *
745
     * @return string
746
     */
747 497
    protected function getRandomId()
0 ignored issues
show
Coding Style introduced by
getRandomId uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
748
    {
749 497
        $idLeft = md5(getmypid() . '.' . time() . '.' . uniqid(mt_rand(), true));
750 497
        $idRight = !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'swift.generated';
751 497
        $id = $idLeft . '@' . $idRight;
752
753
        try {
754 497
            $this->_assertValidId($id);
755
        } catch (Swift_RfcComplianceException $e) {
756
            $id = $idLeft . '@swift.generated';
757
        }
758
759 497
        return $id;
760
    }
761
762
    /**
763
     * generate a new cache-key
764
     *
765
     * @return string
766
     */
767 497
    private function _generateNewCacheKey()
768
    {
769 497
        return md5(uniqid(getmypid() . mt_rand(), true));
770
    }
771
772 14
    private function _readStream(Swift_OutputByteStream $os)
773
    {
774 14
        $string = '';
775 14
        while (false !== $bytes = $os->read(8192)) {
776 14
            $string .= $bytes;
777
        }
778
779 14
        $os->setReadPointer(0);
780
781 14
        return $string;
782
    }
783
784
    /**
785
     * @param string $encoding
786
     */
787 497
    private function _setEncoding($encoding)
788
    {
789 497
        if (!$this->_setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) {
790 492
            $this->_headers->addTextHeader('Content-Transfer-Encoding', $encoding);
791
        }
792 497
    }
793
794
    /**
795
     * @param string $boundary
796
     *
797
     * @throws Swift_RfcComplianceException
798
     */
799 29
    private function _assertValidBoundary($boundary)
800
    {
801 29
        if (!preg_match('/^[a-z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-z0-9\'\(\)\+_\-,\.\/:=\?]$/Di', $boundary)) {
802
            throw new Swift_RfcComplianceException('Mime boundary set is not RFC 2046 compliant.');
803
        }
804 29
    }
805
806 463
    private function _setContentTypeInHeaders($type)
807
    {
808 463
        if (!$this->_setHeaderFieldModel('Content-Type', $type)) {
809 402
            $this->_headers->addParameterizedHeader('Content-Type', $type);
810
        }
811 463
    }
812
813 20
    private function _setNestingLevel($level)
814
    {
815 20
        $this->_nestingLevel = $level;
816 20
    }
817
818
    /**
819
     * @param Swift_Mime_MimeEntity[] $children
820
     *
821
     * @return int
822
     */
823 115
    private function _getCompoundLevel($children)
824
    {
825 115
        $level = 0;
826 115
        foreach ($children as $child) {
827 103
            $level |= $child->getNestingLevel();
828
        }
829
830 115
        return $level;
831
    }
832
833
    /**
834
     * @param Swift_Mime_MimeEntity $child
835
     * @param integer $compoundLevel
836
     *
837
     * @return mixed
838
     */
839 103
    private function _getNeededChildLevel($child, $compoundLevel)
840
    {
841 103
        $filter = array();
842 103
        foreach ($this->_compoundLevelFilters as $bitmask => $rules) {
843 103
            if (($compoundLevel & $bitmask) === $bitmask) {
844 103
                $filter = $rules + $filter;
845
            }
846
        }
847
848 103
        $realLevel = $child->getNestingLevel();
849 103
        $lowercaseType = Swift::strtolowerWithStaticCache($child->getContentType());
850
851 103
        if (isset($filter[$realLevel]) && isset($filter[$realLevel][$lowercaseType])) {
852 7
            return $filter[$realLevel][$lowercaseType];
853
        }
854
855 103
        return $realLevel;
856
    }
857
858
    /**
859
     * @return Swift_Mime_SimpleMimeEntity
860
     */
861 20
    private function _createChild()
862
    {
863 20
        return new self($this->_headers->newInstance(), $this->_encoder, $this->_cache, $this->_emailValidator);
864
    }
865
866
    /**
867
     * @param Swift_Mime_ContentEncoder $encoder
868
     */
869 497
    private function _notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder)
870
    {
871 497
        foreach ($this->_immediateChildren as $child) {
872 11
            $child->encoderChanged($encoder);
873
        }
874 497
    }
875
876
    /**
877
     * @param string $charset
878
     */
879 140
    private function _notifyCharsetChanged($charset)
880
    {
881 140
        $this->_encoder->charsetChanged($charset);
882 140
        $this->_headers->charsetChanged($charset);
883 140
        foreach ($this->_immediateChildren as $child) {
884 7
            $child->charsetChanged($charset);
885
        }
886 140
    }
887
888 115
    private function _sortChildren()
889
    {
890 115
        $shouldSort = false;
891 115
        foreach ($this->_immediateChildren as $child) {
892
            // NOTE: This include alternative parts moved into a related part
893 103
            if ($child->getNestingLevel() == self::LEVEL_ALTERNATIVE) {
894 52
                $shouldSort = true;
895 103
                break;
896
            }
897
        }
898
899
        // Sort in order of preference, if there is one
900 115
        if ($shouldSort) {
901 52
            usort($this->_immediateChildren, array($this, '_childSortAlgorithm'));
902
        }
903 115
    }
904
905
    /**
906
     * @param Swift_Mime_MimeEntity $a
907
     * @param Swift_Mime_MimeEntity $b
908
     *
909
     * @return int
910
     */
911 34
    private function _childSortAlgorithm($a, $b)
912
    {
913 34
        $typePrefs = array();
914
915
        $types = array(
916 34
            Swift::strtolowerWithStaticCache($a->getContentType()),
917 34
            Swift::strtolowerWithStaticCache($b->getContentType())
918
        );
919
920 34
        foreach ($types as $type) {
921 34
            if (isset($this->_alternativePartOrder[$type])) {
922 34
                $typePrefs[] = $this->_alternativePartOrder[$type];
923
            } else {
924 34
                $typePrefs[] = max($this->_alternativePartOrder) + 1;
925
            }
926
        }
927
928 34
        return $typePrefs[0] >= $typePrefs[1] ? 1 : -1;
929
    }
930
931
    // -- Destructor
932
933
    /**
934
     * Empties it's own contents from the cache.
935
     */
936 493
    public function __destruct()
937
    {
938 493
        $this->_cache->clearAll($this->_cacheKey);
939 493
    }
940
941
    /**
942
     * Throws an Exception if the id passed does not comply with RFC 2822.
943
     *
944
     * @param string $id
945
     *
946
     * @throws Swift_RfcComplianceException
947
     */
948 497
    private function _assertValidId($id)
949
    {
950 497 View Code Duplication
        if ($this->_emailValidator->isValidWrapper($id) === 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...
951
            throw new Swift_RfcComplianceException('Invalid ID given <' . $id . '>');
952
        }
953 497
    }
954
955
    /**
956
     * Make a deep copy of object.
957
     */
958 2
    public function __clone()
959
    {
960 2
        if (true === Swift::$useMemorySpool) {
961 2
            $this->_headers = clone $this->_headers;
962 2
            $this->_encoder = clone $this->_encoder;
963
964
            // TODO? do we need this ? -> https://github.com/bmurashin/swiftmailer/commit/ef5c5134e6e02afc2e8ff3287bfa74be1b33dcaf
965
            //$this->_cacheKey = $this->_generateNewCacheKey();
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% 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...
966
            //$this->generateId();
0 ignored issues
show
Unused Code Comprehensibility introduced by
84% 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...
967
968 2
            $children = array();
969 2
            foreach ($this->_children as $pos => $child) {
970
                if (
971 1
                    $child instanceof Swift_Mime_Attachment
972
                    ||
973 1
                    $child instanceof Swift_Mime_EmbeddedFile
974
                ) {
975 1
                    $children[$pos] = $child;
976
                } else {
977 1
                    $children[$pos] = clone $child;
978
                }
979
            }
980 2
            $this->setChildren($children);
981
        }
982 2
    }
983
}
984