Completed
Push — 5.x ( 554335...3f5d86 )
by Lars
16:52
created

_getNeededChildLevel()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 10
nc 6
nop 2
dl 0
loc 18
rs 9.2
c 1
b 0
f 0
ccs 9
cts 9
cp 1
crap 4
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
    /**
128
     * @var string
129
     */
130
    protected $_userContentType;
131
132
    /**
133
     * Create a new SimpleMimeEntity with $headers, $encoder and $cache.
134
     *
135
     * @param Swift_Mime_HeaderSet       $headers
136
     * @param Swift_Mime_ContentEncoder  $encoder
137
     * @param Swift_KeyCache             $cache
138
     * @param Swift_EmailValidatorBridge $emailValidator
139
     */
140 497
    public function __construct(Swift_Mime_HeaderSet $headers, Swift_Mime_ContentEncoder $encoder, Swift_KeyCache $cache, Swift_EmailValidatorBridge $emailValidator)
141
    {
142 497
        $this->_cacheKey = $this->_generateNewCacheKey();
143 497
        $this->_cache = $cache;
144 497
        $this->_headers = $headers;
145 497
        $this->_emailValidator = $emailValidator;
146 497
        $this->setEncoder($encoder);
147 497
        $this->_headers->defineOrdering(array('Content-Type', 'Content-Transfer-Encoding'));
148
149
        // This array specifies that, when the entire MIME document contains
150
        // $compoundLevel, then for each child within $level, if its Content-Type
151
        // is $contentType then it should be treated as if it's level is
152
        // $neededLevel instead.  I tried to write that unambiguously! :-\
153
        // Data Structure:
154
        // array (
155
        //   $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...
156
        //     $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...
157
        //       $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...
158
        //     )
159
        //   )
160
        // )
161
162 497
        $this->_compoundLevelFilters = array(
163 497
            (self::LEVEL_ALTERNATIVE + self::LEVEL_RELATED) => array(
164 497
                self::LEVEL_ALTERNATIVE => array(
165 497
                    'text/plain' => self::LEVEL_ALTERNATIVE,
166 497
                    'text/html' => self::LEVEL_RELATED,
167 497
                ),
168 497
            ),
169
        );
170
171 497
        $this->_id = $this->getRandomId();
172 497
    }
173
174
    /**
175
     * Generate a new Content-ID or Message-ID for this MIME entity.
176
     *
177
     * @return string
178
     */
179 16
    public function generateId()
180
    {
181 16
        $this->setId($this->getRandomId());
182
183 16
        return $this->_id;
184
    }
185
186
    /**
187
     * Get the {@link Swift_Mime_HeaderSet} for this entity.
188
     *
189
     * @return Swift_Mime_HeaderSet
190
     */
191 360
    public function getHeaders()
192
    {
193 360
        return $this->_headers;
194
    }
195
196
    /**
197
     * Get the nesting level of this entity.
198
     *
199
     * @see LEVEL_TOP, LEVEL_MIXED, LEVEL_RELATED, LEVEL_ALTERNATIVE
200
     *
201
     * @return int
202
     */
203 20
    public function getNestingLevel()
204
    {
205 20
        return $this->_nestingLevel;
206
    }
207
208
    /**
209
     * Get the Content-type of this entity.
210
     *
211
     * @return string
212
     */
213 53
    public function getContentType()
214
    {
215 53
        return $this->_getHeaderFieldModel('Content-Type');
216
    }
217
218
    /**
219
     * Set the Content-type of this entity.
220
     *
221
     * @param string $type
222
     *
223
     * @return Swift_Mime_SimpleMimeEntity
224
     */
225 452
    public function setContentType($type)
226
    {
227 452
        $this->_setContentTypeInHeaders($type);
228
        // Keep track of the value so that if the content-type changes automatically
229
        // due to added child entities, it can be restored if they are later removed
230 452
        $this->_userContentType = $type;
231
232 452
        return $this;
233
    }
234
235
    /**
236
     * Get the CID of this entity.
237
     *
238
     * The CID will only be present in headers if a Content-ID header is present.
239
     *
240
     * @return string
241
     */
242 307
    public function getId()
243
    {
244 307
        $tmp = (array) $this->_getHeaderFieldModel($this->_getIdField());
245
246 307
        return $this->_headers->has($this->_getIdField()) ? current($tmp) : $this->_id;
247
    }
248
249
    /**
250
     * Set the CID of this entity.
251
     *
252
     * @param string $id
253
     *
254
     * @return Swift_Mime_SimpleMimeEntity
255
     */
256 310
    public function setId($id)
257
    {
258 310
        if (!$this->_setHeaderFieldModel($this->_getIdField(), $id)) {
259 303
            $this->_headers->addIdHeader($this->_getIdField(), $id);
260 303
        }
261 310
        $this->_id = $id;
262
263 310
        return $this;
264
    }
265
266
    /**
267
     * Get the description of this entity.
268
     *
269
     * This value comes from the Content-Description header if set.
270
     *
271
     * @return string
272
     */
273 5
    public function getDescription()
274
    {
275 5
        return $this->_getHeaderFieldModel('Content-Description');
276
    }
277
278
    /**
279
     * Set the description of this entity.
280
     *
281
     * This method sets a value in the Content-ID header.
282
     *
283
     * @param string $description
284
     *
285
     * @return Swift_Mime_SimpleMimeEntity
286
     */
287 15
    public function setDescription($description)
288
    {
289 15
        if (!$this->_setHeaderFieldModel('Content-Description', $description)) {
290 10
            $this->_headers->addTextHeader('Content-Description', $description);
291 10
        }
292
293 15
        return $this;
294
    }
295
296
    /**
297
     * Get the maximum line length of the body of this entity.
298
     *
299
     * @return int
300
     */
301 114
    public function getMaxLineLength()
302
    {
303 114
        return $this->_maxLineLength;
304
    }
305
306
    /**
307
     * Set the maximum line length of lines in this body.
308
     *
309
     * Though not enforced by the library, lines should not exceed 1000 chars.
310
     *
311
     * @param int $length
312
     *
313
     * @return Swift_Mime_SimpleMimeEntity
314
     */
315 32
    public function setMaxLineLength($length)
316
    {
317 32
        $this->_maxLineLength = $length;
318
319 32
        return $this;
320
    }
321
322
    /**
323
     * Get all children added to this entity.
324
     *
325
     * @return Swift_Mime_MimeEntity[]
326
     */
327 147
    public function getChildren()
328
    {
329 147
        return $this->_children;
330
    }
331
332
    /**
333
     * Set all children of this entity.
334
     *
335
     * @param Swift_Mime_MimeEntity[] $children
336
     * @param int                     $compoundLevel For internal use only
337
     *
338
     * @return Swift_Mime_SimpleMimeEntity
339
     */
340 117
    public function setChildren(array $children, $compoundLevel = null)
341
    {
342
        // TODO: Try to refactor this logic
343
344 117
        $compoundLevel = isset($compoundLevel) ? $compoundLevel : $this->_getCompoundLevel($children);
345 117
        $immediateChildren = array();
346 117
        $grandchildren = array();
347 117
        $newContentType = $this->_userContentType;
348
349 117
        foreach ($children as $child) {
350 103
            $level = $this->_getNeededChildLevel($child, $compoundLevel);
351 103
            if (empty($immediateChildren)) {
352
                //first iteration
353 103
                $immediateChildren = array($child);
354 103
            } else {
355 56
                $nextLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel);
356 56
                if ($nextLevel === $level) {
357 40
                    $immediateChildren[] = $child;
358 56
                } elseif ($level < $nextLevel) {
359
                    // Re-assign immediateChildren to grandchildren
360 13
                    $grandchildren = array_merge($grandchildren, $immediateChildren);
361
                    // Set new children
362 13
                    $immediateChildren = array($child);
363 13
                } else {
364 12
                    $grandchildren[] = $child;
365
                }
366
            }
367 117
        }
368
369 117
        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...
370 103
            $lowestLevel = $this->_getNeededChildLevel($immediateChildren[0], $compoundLevel);
371
372
            // Determine which composite media type is needed to accommodate the
373
            // immediate children
374 103
            foreach ($this->_compositeRanges as $mediaType => $range) {
375 103
                if ($lowestLevel > $range[0] && $lowestLevel <= $range[1]) {
376 77
                    $newContentType = $mediaType;
377
378 77
                    break;
379
                }
380 103
            }
381
382
            // Put any grandchildren in a sub-part
383 103
            if (!empty($grandchildren)) {
384 20
                $subentity = $this->_createChild();
385 20
                $subentity->_setNestingLevel($lowestLevel);
386 20
                $subentity->setChildren($grandchildren, $compoundLevel);
387 20
                array_unshift($immediateChildren, $subentity);
388 20
            }
389 103
        }
390
391 117
        $this->_immediateChildren = $immediateChildren;
392 117
        $this->_children = $children;
393 117
        $this->_setContentTypeInHeaders($newContentType);
394 117
        $this->_fixHeaders();
395 117
        $this->_sortChildren();
396
397 117
        return $this;
398
    }
399
400
    /**
401
     * Get the body of this entity as a string.
402
     *
403
     * @return string
404
     */
405 125
    public function getBody()
406
    {
407 125
        return $this->_body instanceof Swift_OutputByteStream ? $this->_readStream($this->_body) : $this->_body;
408
    }
409
410
    /**
411
     * Set the body of this entity, either as a string, or as an instance of
412
     * {@link Swift_OutputByteStream}.
413
     *
414
     * @param mixed  $body
415
     * @param string $contentType optional
416
     *
417
     * @return Swift_Mime_SimpleMimeEntity
418
     */
419 218
    public function setBody($body, $contentType = null)
420
    {
421 218
        if ($body !== $this->_body) {
422 157
            $this->_clearCache();
423 157
        }
424
425 218
        $this->_body = $body;
426 218
        if (isset($contentType)) {
427 17
            $this->setContentType($contentType);
428 17
        }
429
430 218
        return $this;
431
    }
432
433
    /**
434
     * Get the encoder used for the body of this entity.
435
     *
436
     * @return Swift_Mime_ContentEncoder
437
     */
438 27
    public function getEncoder()
439
    {
440 27
        return $this->_encoder;
441
    }
442
443
    /**
444
     * Set the encoder used for the body of this entity.
445
     *
446
     * @param Swift_Mime_ContentEncoder $encoder
447
     *
448
     * @return Swift_Mime_SimpleMimeEntity
449
     */
450 497
    public function setEncoder(Swift_Mime_ContentEncoder $encoder)
451
    {
452 497
        if ($encoder !== $this->_encoder) {
453 497
            $this->_clearCache();
454 497
        }
455
456 497
        $this->_encoder = $encoder;
457 497
        $this->_setEncoding($encoder->getName());
458 497
        $this->_notifyEncoderChanged($encoder);
459
460 497
        return $this;
461
    }
462
463
    /**
464
     * Get the boundary used to separate children in this entity.
465
     *
466
     * @return string
467
     */
468 118
    public function getBoundary()
469
    {
470 118
        if (!isset($this->_boundary)) {
471 102
            $this->_boundary = '_=_swift_v5_' . time() . '_' . md5(getmypid() . mt_rand() . uniqid('', true)) . '_=_';
472 102
        }
473
474 118
        return $this->_boundary;
475
    }
476
477
    /**
478
     * Set the boundary used to separate children in this entity.
479
     *
480
     * @param string $boundary
481
     *
482
     * @throws Swift_RfcComplianceException
483
     *
484
     * @return Swift_Mime_SimpleMimeEntity
485
     */
486 29
    public function setBoundary($boundary)
487
    {
488 29
        $this->_assertValidBoundary($boundary);
489 29
        $this->_boundary = $boundary;
490
491 29
        return $this;
492
    }
493
494
    /**
495
     * Receive notification that the charset of this entity, or a parent entity
496
     * has changed.
497
     *
498
     * @param string $charset
499
     */
500 140
    public function charsetChanged($charset)
501
    {
502 140
        $this->_notifyCharsetChanged($charset);
503 140
    }
504
505
    /**
506
     * Receive notification that the encoder of this entity or a parent entity
507
     * has changed.
508
     *
509
     * @param Swift_Mime_ContentEncoder $encoder
510
     */
511 6
    public function encoderChanged(Swift_Mime_ContentEncoder $encoder)
512
    {
513 6
        $this->_notifyEncoderChanged($encoder);
514 6
    }
515
516
    /**
517
     * Get this entire entity as a string.
518
     *
519
     * @return string
520
     */
521 183
    public function toString()
522
    {
523 183
        $string = $this->_headers->toString();
524 183
        $string .= $this->_bodyToString();
525
526 183
        return $string;
527
    }
528
529
    /**
530
     * Get this entire entity as a string.
531
     *
532
     * @return string
533
     */
534 183
    protected function _bodyToString()
535
    {
536 183
        $string = '';
537
538 183
        if (
539 95
            isset($this->_body) 
540 7
            && 
541 7
            empty($this->_immediateChildren)
542 90
        ) {
543 90
            if ($this->_cache->hasKey($this->_cacheKey, 'body')) {
544
                $body = $this->_cache->getString($this->_cacheKey, 'body');
545 95
            } else {
546 95
                $body = "\r\n" . $this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength());
547
                $this->_cache->setString($this->_cacheKey, 'body', $body, Swift_KeyCache::MODE_WRITE);
548 183
            }
549 39
            $string .= $body;
550 39
        }
551 39
552 39
        if (!empty($this->_immediateChildren)) {
553 39
            foreach ($this->_immediateChildren as $child) {
554 39
                $string .= "\r\n\r\n--" . $this->getBoundary() . "\r\n";
555
                $string .= $child->toString();
556 183
            }
557
            $string .= "\r\n\r\n--" . $this->getBoundary() . "--\r\n";
558
        }
559
560
        return $string;
561
    }
562
563
    /**
564
     * Returns a string representation of this object.
565
     *
566
     * @see toString()
567
     *
568
     * @return string
569
     */
570
    public function __toString()
571
    {
572
        return $this->toString();
573
    }
574
575
    /**
576 37
     * Write this entire entity to a {@see Swift_InputByteStream}.
577
     *
578 37
     * @param Swift_InputByteStream
579 37
     */
580
    public function toByteStream(Swift_InputByteStream $is)
581 37
    {
582 37
        $is->write($this->_headers->toString());
583
        $is->commit();
584
585
        $this->_bodyToByteStream($is);
586
    }
587
588
    /**
589 37
     * Write this entire entity to a {@link Swift_InputByteStream}.
590
     *
591 37
     * @param Swift_InputByteStream
592 37
     */
593 27
    protected function _bodyToByteStream(Swift_InputByteStream $is)
594 8
    {
595 8
        if (empty($this->_immediateChildren)) {
596 22
            if (isset($this->_body)) {
597 22
                if ($this->_cache->hasKey($this->_cacheKey, 'body')) {
598 17
                    $this->_cache->exportToByteStream($this->_cacheKey, 'body', $is);
599 17
                } else {
600
                    $cacheIs = $this->_cache->getInputByteStream($this->_cacheKey, 'body');
601 22
                    if ($cacheIs) {
602
                        $is->bind($cacheIs);
603 22
                    }
604 14
605
                    $is->write("\r\n");
606 14
607 14
                    if ($this->_body instanceof Swift_OutputByteStream) {
608 22
                        $this->_body->setReadPointer(0);
609
610
                        $this->_encoder->encodeByteStream($this->_body, $is, 0, $this->getMaxLineLength());
611 22
                    } else {
612 17
                        $is->write($this->_encoder->encodeString($this->getBody(), 0, $this->getMaxLineLength()));
613 17
                    }
614
615 27
                    if ($cacheIs) {
616 37
                        $is->unbind($cacheIs);
617
                    }
618 37
                }
619 9
            }
620 9
        }
621 9
622 9
        if (!empty($this->_immediateChildren)) {
623 9
            foreach ($this->_immediateChildren as $child) {
624 9
                $is->write("\r\n\r\n--" . $this->getBoundary() . "\r\n");
625 37
                $child->toByteStream($is);
626
            }
627
            $is->write("\r\n\r\n--" . $this->getBoundary() . "--\r\n");
628
        }
629
    }
630 106
631
    /**
632 106
     * Get the name of the header that provides the ID of this entity.
633
     */
634
    protected function _getIdField()
635
    {
636
        return 'Content-ID';
637
    }
638
639
    /**
640
     * Get the model data (usually an array or a string) for $field.
641
     *
642 317
     * @param string $field
643
     *
644 317
     * @return string
645 152
     */
646
    protected function _getHeaderFieldModel($field)
647 300
    {
648
        if ($this->_headers->has($field)) {
649
            return $this->_headers->get($field)->getFieldBodyModel();
650
        } else {
651
            return null;
652
        }
653
    }
654
655
    /**
656
     * Set the model data for $field.
657
     *
658
     * @param string $field
659 497
     * @param $model
660
     *
661 497
     * @return bool
662 247
     */
663
    protected function _setHeaderFieldModel($field, $model)
664 247
    {
665
        if ($this->_headers->has($field)) {
666
            $this->_headers->get($field)->setFieldBodyModel($model);
667 496
668
            return true;
669
        }
670
671
        return false;
672
    }
673
674
    /**
675
     * Get the parameter value of $parameter on $field header.
676
     * @param string $field
677 163
     * @param string $parameter
678
     *
679 163
     * @return string
680 131
     */
681
    protected function _getHeaderParameter($field, $parameter)
682 32
    {
683
        if ($this->_headers->has($field)) {
684
            return $this->_headers->get($field)->getParameter($parameter);
0 ignored issues
show
Bug introduced by
The method getParameter does only exist in Swift_Mime_ParameterizedHeader, but not in Swift_Mime_Headers_MailboxHeader.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
685
        }
686
    }
687
688
    /**
689
     * Set the parameter value of $parameter on $field header.
690
     * @param string $field
691
     * @param string $parameter
692 238
     * @param        $value
693
     *
694 238
     * @return bool
695 189
     */
696
    protected function _setHeaderParameter($field, $parameter, $value)
697 189
    {
698
        if ($this->_headers->has($field)) {
699
            $this->_headers->get($field)->setParameter($parameter, $value);
0 ignored issues
show
Bug introduced by
The method setParameter does only exist in Swift_Mime_ParameterizedHeader, but not in Swift_Mime_Headers_MailboxHeader.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
700 55
701
            return true;
702
        }
703
704
        return false;
705
    }
706 117
707
    /**
708 117
     * Re-evaluate what content type and encoding should be used on this entity.
709 103
     */
710 103
    protected function _fixHeaders()
711 103
    {
712 24
        if (count($this->_immediateChildren)) {
713 24
            $this->_setHeaderParameter('Content-Type', 'boundary', $this->getBoundary());
714
            $this->_headers->remove('Content-Transfer-Encoding');
715 117
        } else {
716
            $this->_setHeaderParameter('Content-Type', 'boundary', null);
717
            $this->_setEncoding($this->_encoder->getName());
718
        }
719
    }
720
721
    /**
722 17
     * Get the KeyCache used in this entity.
723
     *
724 17
     * @return Swift_KeyCache
725
     */
726
    protected function _getCache()
727
    {
728
        return $this->_cache;
729
    }
730
731
    /**
732 17
     * Get the email-validator.
733
     *
734 17
     * @return Swift_EmailValidatorBridge
735
     */
736
    protected function _getEmailValidator()
737
    {
738
        return $this->_emailValidator;
739
    }
740 497
741
    /**
742 497
     * Empty the KeyCache for this entity.
743 497
     */
744
    protected function _clearCache()
745
    {
746
        $this->_cache->clearKey($this->_cacheKey, 'body');
747
    }
748
749
    /**
750 497
     * Returns a random Content-ID or Message-ID.
751
     *
752 497
     * @return string
753 497
     */
754 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...
755
    {
756
        $idLeft = $this->_generateNewCacheKey();
757 497
        $idRight = !empty($_SERVER['SERVER_NAME']) ? $_SERVER['SERVER_NAME'] : 'swift.generated';
758 497
        $id = $idLeft . '@' . $idRight;
759
760
        try {
761
            $this->_assertValidId($id);
762 497
        } catch (Swift_RfcComplianceException $e) {
763
            $id = $idLeft . '@swift.generated';
764
        }
765
766
        return $id;
767
    }
768
769
    /**
770 497
     * generate a new cache-key
771
     *
772 497
     * @return string
773
     */
774
    private function _generateNewCacheKey()
775 14
    {
776
        return md5(getmypid() . '.' . time() . '.' . uniqid(mt_rand(), true));
777 14
    }
778 14
779 14
    private function _readStream(Swift_OutputByteStream $os)
780 14
    {
781
        $string = '';
782 14
        while (false !== $bytes = $os->read(8192)) {
783
            $string .= $bytes;
784 14
        }
785
786
        $os->setReadPointer(0);
787
788
        return $string;
789
    }
790 497
791
    /**
792 497
     * @param string $encoding
793 492
     */
794 492
    private function _setEncoding($encoding)
795 497
    {
796
        if (!$this->_setHeaderFieldModel('Content-Transfer-Encoding', $encoding)) {
797
            $this->_headers->addTextHeader('Content-Transfer-Encoding', $encoding);
798
        }
799
    }
800
801
    /**
802 29
     * @param string $boundary
803
     *
804 29
     * @throws Swift_RfcComplianceException
805
     */
806
    private function _assertValidBoundary($boundary)
807 29
    {
808
        if (!preg_match('/^[a-z0-9\'\(\)\+_\-,\.\/:=\?\ ]{0,69}[a-z0-9\'\(\)\+_\-,\.\/:=\?]$/Di', $boundary)) {
809 463
            throw new Swift_RfcComplianceException('Mime boundary set is not RFC 2046 compliant.');
810
        }
811 463
    }
812 402
813 402
    private function _setContentTypeInHeaders($type)
814 463
    {
815
        if (!$this->_setHeaderFieldModel('Content-Type', $type)) {
816 20
            $this->_headers->addParameterizedHeader('Content-Type', $type);
817
        }
818 20
    }
819 20
820
    private function _setNestingLevel($level)
821
    {
822
        $this->_nestingLevel = $level;
823
    }
824
825
    /**
826 117
     * @param Swift_Mime_MimeEntity[] $children
827
     *
828 117
     * @return int
829 117
     */
830 103
    private function _getCompoundLevel($children)
831 117
    {
832
        $level = 0;
833 117
        foreach ($children as $child) {
834
            $level |= $child->getNestingLevel();
835
        }
836
837
        return $level;
838
    }
839
840
    /**
841
     * @param Swift_Mime_MimeEntity $child
842 103
     * @param integer $compoundLevel
843
     *
844 103
     * @return int
845 103
     */
846 103
    private function _getNeededChildLevel($child, $compoundLevel)
847 12
    {
848 12
        $filter = array();
849 103
        foreach ($this->_compoundLevelFilters as $bitmask => $rules) {
850
            if (($compoundLevel & $bitmask) === $bitmask) {
851 103
                $filter = $rules + $filter;
852 103
            }
853
        }
854 103
855 7
        $realLevel = $child->getNestingLevel();
856
        $lowercaseType = Swift::strtolowerWithStaticCache($child->getContentType());
857
858 103
        if (isset($filter[$realLevel], $filter[$realLevel][$lowercaseType])) {
859
            return (int)$filter[$realLevel][$lowercaseType];
860
        }
861
862
        return (int)$realLevel;
863
    }
864 20
865
    /**
866 20
     * @return Swift_Mime_SimpleMimeEntity
867
     */
868
    private function _createChild()
869
    {
870
        return new self($this->_headers->newInstance(), $this->_encoder, $this->_cache, $this->_emailValidator);
871
    }
872 497
873
    /**
874 497
     * @param Swift_Mime_ContentEncoder $encoder
875 11
     */
876 497
    private function _notifyEncoderChanged(Swift_Mime_ContentEncoder $encoder)
877 497
    {
878
        foreach ($this->_immediateChildren as $child) {
879
            $child->encoderChanged($encoder);
880
        }
881
    }
882 140
883
    /**
884 140
     * @param string $charset
885 140
     */
886 140
    private function _notifyCharsetChanged($charset)
887 7
    {
888 140
        $this->_encoder->charsetChanged($charset);
889 140
        $this->_headers->charsetChanged($charset);
890
        foreach ($this->_immediateChildren as $child) {
891 117
            $child->charsetChanged($charset);
892
        }
893 117
    }
894 117
895
    private function _sortChildren()
896 103
    {
897 52
        $shouldSort = false;
898 52
        foreach ($this->_immediateChildren as $child) {
899
            // NOTE: This include alternative parts moved into a related part
900 117
            if ($child->getNestingLevel() === self::LEVEL_ALTERNATIVE) {
901
                $shouldSort = true;
902
                break;
903 117
            }
904 52
        }
905 52
906 117
        // Sort in order of preference, if there is one
907
        if ($shouldSort) {
908
            usort($this->_immediateChildren, array($this, '_childSortAlgorithm'));
909
        }
910
    }
911
912
    /**
913
     * @param Swift_Mime_MimeEntity $a
914 34
     * @param Swift_Mime_MimeEntity $b
915
     *
916 34
     * @return int
917
     */
918
    private function _childSortAlgorithm($a, $b)
919 34
    {
920 34
        $typePrefs = array();
921 34
922
        $types = array(
923 34
            Swift::strtolowerWithStaticCache($a->getContentType()),
924 34
            Swift::strtolowerWithStaticCache($b->getContentType())
925 34
        );
926 34
927 7
        foreach ($types as $type) {
928
            if (isset($this->_alternativePartOrder[$type])) {
929 34
                $typePrefs[] = $this->_alternativePartOrder[$type];
930
            } else {
931 34
                $typePrefs[] = max($this->_alternativePartOrder) + 1;
932
            }
933
        }
934
935
        return $typePrefs[0] >= $typePrefs[1] ? 1 : -1;
936
    }
937 495
938
    /**
939 495
     * Empties it's own contents from the cache.
940 495
     */
941
    public function __destruct()
942
    {
943
        $this->_cache->clearAll($this->_cacheKey);
944
    }
945
946
    /**
947
     * Throws an Exception if the id passed does not comply with RFC 2822.
948
     *
949 497
     * @param string $id
950
     *
951 497
     * @throws Swift_RfcComplianceException
952
     */
953
    private function _assertValidId($id)
954 497
    {
955 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...
956
            throw new Swift_RfcComplianceException('Invalid ID given <' . $id . '>');
957
        }
958
    }
959 5
960
    /**
961 5
     * Make a deep copy of object.
962 5
     */
963
    public function __clone()
964
    {
965
        $this->_headers = clone $this->_headers;
966
        $this->_encoder = clone $this->_encoder;
967
968 5
        // TODO? do we need this ? -> https://github.com/bmurashin/swiftmailer/commit/ef5c5134e6e02afc2e8ff3287bfa74be1b33dcaf
969 5
        //$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...
970 2
        //$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...
971 1
972 1
        $children = array();
973 1
        foreach ($this->_children as $pos => $child) {
974
            if ($child instanceof Swift_Mime_Attachment || $child instanceof Swift_Mime_EmbeddedFile) {
975 5
                $children[$pos] = $child;
976 5
            } else {
977 5
                $children[$pos] = clone $child;
978
            }
979
        }
980
        $this->setChildren($children);
981
    }
982
}
983