Completed
Pull Request — master (#48)
by Frederik
02:18
created

MimeMessageFactory   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 127
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 13

Test Coverage

Coverage 98.31%

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 13
dl 0
loc 127
ccs 58
cts 59
cp 0.9831
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
B createMessage() 0 25 3
B createPartStream() 0 36 6
A createHeaderLines() 0 14 1
B pickOptimalCharset() 0 29 6
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail;
5
6
use Genkgo\Mail\Header\FixedHeader;
7
use Genkgo\Mail\Header\HeaderLine;
8
use Genkgo\Mail\Header\HeaderName;
9
use Genkgo\Mail\Header\HeaderValueParameter;
10
use Genkgo\Mail\Mime\MultiPartInterface;
11
use Genkgo\Mail\Mime\PartInterface;
12
use Genkgo\Mail\Stream\ConcatenatedStream;
13
use Genkgo\Mail\Stream\StringStream;
14
15
final class MimeMessageFactory
16
{
17
    /**
18
     * @param PartInterface $part
19
     * @return MessageInterface
20
     */
21 7
    public function createMessage(PartInterface $part): MessageInterface
22
    {
23 7
        $message = new GenericMessage();
24
25 7
        $part = $this->pickOptimalCharset($part);
26
27 7
        foreach ($part->getHeaders() as $header) {
28 7
            $message = $message->withHeader($header);
29
        }
30
31 7
        if ($part instanceof MultiPartInterface) {
32 4
            $message = $message->withBody(
33 4
                new ConcatenatedStream(
34 4
                    new \ArrayObject([
0 ignored issues
show
Documentation introduced by
new \ArrayObject(array(n...tStream($part, false))) is of type object<ArrayObject>, but the function expects a object<Genkgo\Mail\Stream\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
35 4
                        new StringStream("This is a multipart message in MIME format.\r\n"),
36 4
                        $this->createPartStream($part, false)
37
                    ])
38
                )
39
            );
40
        } else {
41 3
            $message = $message->withBody($this->createPartStream($part, false));
42
        }
43
44 7
        return $message;
45
    }
46
47
    /**
48
     * @param PartInterface $part
49
     * @param bool $headers
50
     * @return StreamInterface
51
     */
52 7
    private function createPartStream(PartInterface $part, bool $headers = true): StreamInterface
53
    {
54 7
        $part = $this->pickOptimalCharset($part);
55
56 7
        $partBody = $part->getBody();
57 7
        $streams = new \ArrayObject();
58 7
        if ($headers) {
59 4
            $streams->append(new StringStream($this->createHeaderLines($part)));
60 4
            $streams->append(new StringStream("\r\n\r\n"));
61
        }
62
63 7
        $streams->append($partBody);
64
65 7
        if ($part instanceof MultiPartInterface) {
66 4
            $childParts = $part->getParts();
67 4
            $boundary = $part->getBoundary();
68
69 4
            foreach ($childParts as $childPart) {
70 4
                $streams->append(new StringStream("\r\n"));
71 4
                $streams->append(new StringStream('--' . (string) $boundary));
72 4
                $streams->append(new StringStream("\r\n"));
73 4
                $streams->append($this->createPartStream($childPart));
74
            }
75
76 4
            if ($childParts) {
77 4
                $streams->append(new StringStream('--' . (string) $boundary . '--'));
78 4
                $streams->append(new StringStream("\r\n"));
79
            }
80
        }
81
82 7
        if ($partBody->getSize() !== 0) {
83 6
            $streams->append(new StringStream("\r\n"));
84
        }
85
86 7
        return new ConcatenatedStream($streams);
0 ignored issues
show
Documentation introduced by
$streams is of type object<ArrayObject>, but the function expects a object<Genkgo\Mail\Stream\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
87
    }
88
89
    /**
90
     * @param PartInterface $part
91
     * @return string
92
     */
93 4
    private function createHeaderLines(PartInterface $part): string
94
    {
95 4
        return \implode(
96 4
            "\r\n",
97 4
            \array_values(
98 4
                \array_map(
99 4
                    function (HeaderInterface $header) {
100 4
                        return (string) (new HeaderLine($header));
101 4
                    },
102 4
                    $part->getHeaders()
103
                )
104
            )
105
        );
106
    }
107
108
    /**
109
     * @param PartInterface $part
110
     * @return PartInterface
111
     */
112 7
    private function pickOptimalCharset(PartInterface $part): PartInterface
113
    {
114
        try {
115 7
            $transferEncoding = (string) $part->getHeader('Content-Transfer-Encoding')->getValue();
116 4
        } catch (\UnexpectedValueException $e) {
117 4
            return $part;
118
        }
119
120 7
        $contentTypeHeader = $part->getHeader('Content-Type')->getValue();
121 7
        if ($transferEncoding !== '7bit' || \substr((string)$contentTypeHeader, 0, 5) !== 'text/') {
122 3
            return $part;
123
        }
124
125
        try {
126 7
            if ($contentTypeHeader->getParameter('charset')->getValue() === 'us-ascii') {
127 7
                return $part;
128
            }
129
        } catch (\UnexpectedValueException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
130
        }
131
132 7
        return $part->withHeader(
133 7
            new FixedHeader(
134 7
                new HeaderName('Content-Type'),
135 7
                $contentTypeHeader->withParameter(
136 7
                    new HeaderValueParameter('charset', 'us-ascii')
137
                )
138
            )
139
        );
140
    }
141
}
142