Completed
Pull Request — master (#18)
by Frederik
01:29
created

MimeMessageFactory   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 127
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 12

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 16
lcom 1
cbo 12
dl 0
loc 127
ccs 59
cts 59
cp 1
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\GenericHeader;
7
use Genkgo\Mail\Header\HeaderLine;
8
use Genkgo\Mail\Header\HeaderValueParameter;
9
use Genkgo\Mail\Mime\MultiPartInterface;
10
use Genkgo\Mail\Mime\PartInterface;
11
use Genkgo\Mail\Stream\ConcatenatedStream;
12
use Genkgo\Mail\Stream\StringStream;
13
14
/**
15
 * Class MimeMessageFactory
16
 * @package Genkgo\Mail
17
 */
18
final class MimeMessageFactory
19
{
20
    /**
21
     * @param PartInterface $part
22
     * @return MessageInterface
23
     */
24 4
    public function createMessage(PartInterface $part): MessageInterface
25
    {
26 4
        $message = new GenericMessage();
27
28 4
        $part = $this->pickOptimalCharset($part);
29
30 4
        foreach ($part->getHeaders() as $header) {
31 4
            $message = $message->withHeader($header);
32
        }
33
34 4
        if ($part instanceof MultiPartInterface) {
35 2
            $message = $message->withBody(
36 2
                new ConcatenatedStream(
37 2
                    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...
38 2
                        new StringStream("This is a multipart message in MIME format.\r\n"),
39 2
                        $this->createPartStream($part, false)
40
                    ])
41
                )
42
            );
43
        } else {
44 2
            $message = $message->withBody($this->createPartStream($part, false));
45
        }
46
47 4
        return $message;
48
    }
49
50
    /**
51
     * @param PartInterface $part
52
     * @param bool $headers
53
     * @return StreamInterface
54
     */
55 4
    private function createPartStream(PartInterface $part, bool $headers = true): StreamInterface
56
    {
57 4
        $part = $this->pickOptimalCharset($part);
58
59 4
        $partBody = $part->getBody();
60 4
        $streams = new \ArrayObject();
61 4
        if ($headers) {
62 2
            $streams->append(new StringStream($this->createHeaderLines($part)));
63 2
            $streams->append(new StringStream("\r\n\r\n"));
64
        }
65
66 4
        $streams->append($partBody);
67
68 4
        if ($part instanceof MultiPartInterface) {
69 2
            $childParts = $part->getParts();
70 2
            $boundary = $part->getBoundary();
71
72 2
            foreach ($childParts as $childPart) {
73 2
                $streams->append(new StringStream("\r\n"));
74 2
                $streams->append(new StringStream('--' . (string) $boundary));
75 2
                $streams->append(new StringStream("\r\n"));
76 2
                $streams->append($this->createPartStream($childPart));
77
            }
78
79 2
            if ($childParts) {
80 2
                $streams->append(new StringStream('--' . (string) $boundary . '--'));
81 2
                $streams->append(new StringStream("\r\n"));
82
            }
83
        }
84
85 4
        if ($partBody->getSize() !== 0) {
86 3
            $streams->append(new StringStream("\r\n"));
87
        }
88
89 4
        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...
90
    }
91
92
    /**
93
     * @param PartInterface $part
94
     * @return string
95
     */
96 2
    private function createHeaderLines(PartInterface $part): string
97
    {
98 2
        return implode(
99 2
            "\r\n",
100 2
            array_values(
101 2
                array_map(
102 2
                    function (HeaderInterface $header) {
103 2
                        return (string) (new HeaderLine($header));
104 2
                    },
105 2
                    $part->getHeaders()
106
                )
107
            )
108
        );
109
    }
110
111
    /**
112
     * @param PartInterface $part
113
     * @return PartInterface
114
     */
115 4
    private function pickOptimalCharset(PartInterface $part): PartInterface
116
    {
117
        try {
118 4
            $transferEncoding = (string) $part->getHeader('Content-Transfer-Encoding')->getValue();
119 2
        } catch (\UnexpectedValueException $e) {
120 2
            return $part;
121
        }
122
123 4
        $contentTypeHeader = $part->getHeader('Content-Type')->getValue();
124 4
        if ($transferEncoding !== '7bit' || substr((string)$contentTypeHeader, 0, 5) !== 'text/') {
125 2
            return $part;
126
        }
127
128
        try {
129 4
            if ($contentTypeHeader->getParameter('charset')->getValue() === 'us-ascii') {
130 4
                return $part;
131
            }
132 2
        } catch (\UnexpectedValueException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
133
        }
134
135 4
        return $part->withHeader(
136 4
            new GenericHeader(
137 4
                'Content-Type',
138 4
                (string) $contentTypeHeader->withParameter(
139 4
                    new HeaderValueParameter('charset', 'us-ascii')
140
                )
141
            )
142
        );
143
    }
144
}