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

MultiPart::fromMessage()   C

Complexity

Conditions 12
Paths 14

Size

Total Lines 61
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 31
CRAP Score 12.0988

Importance

Changes 0
Metric Value
dl 0
loc 61
ccs 31
cts 34
cp 0.9118
rs 6.2855
c 0
b 0
f 0
cc 12
eloc 36
nc 14
nop 1
crap 12.0988

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Mime;
5
6
use Genkgo\Mail\GenericMessage;
7
use Genkgo\Mail\Header\ContentType;
8
use Genkgo\Mail\Header\ParsedHeader;
9
use Genkgo\Mail\Header\HeaderValueParameter;
10
use Genkgo\Mail\HeaderInterface;
11
use Genkgo\Mail\MessageInterface;
12
use Genkgo\Mail\Stream\EmptyStream;
13
use Genkgo\Mail\Stream\LineIterator;
14
use Genkgo\Mail\StreamInterface;
15
16
final class MultiPart implements MultiPartInterface
17
{
18
    /**
19
     * @var PartInterface
20
     */
21
    private $decoratedPart;
22
23
    /**
24
     * @var Boundary
25
     */
26
    private $boundary;
27
28
    /**
29
     * @var iterable|PartInterface[]
30
     */
31
    private $parts = [];
32
33
    /**
34
     * @param Boundary $boundary
35
     * @param ContentType $contentType
36
     */
37 14
    public function __construct(Boundary $boundary, ContentType $contentType)
38
    {
39 14
        $this->boundary = $boundary;
40
41 14
        if (\substr((string)$contentType->getValue(), 0, 10) !== 'multipart/') {
42 1
            throw new \InvalidArgumentException('Content type must be of type multipart/type');
43
        }
44
45 13
        $this->decoratedPart = (new GenericPart())
46 13
            ->withHeader(
47 13
                new ParsedHeader(
48 13
                    $contentType->getName(),
49 13
                    $contentType->getValue()
50 13
                        ->withParameter(
51 13
                            new HeaderValueParameter(
52 13
                                'boundary',
53 13
                                (string)$boundary
54
                            )
55
                        )
56
                )
57
            );
58 13
    }
59
60
    /**
61
     * @return iterable
62
     */
63 5
    public function getHeaders(): iterable
64
    {
65 5
        return $this->decoratedPart->getHeaders();
66
    }
67
68
    /**
69
     * @param string $name
70
     * @return bool
71
     */
72 3
    public function hasHeader(string $name): bool
73
    {
74 3
        return $this->decoratedPart->hasHeader($name);
75
    }
76
77
    /**
78
     * @param string $name
79
     * @return HeaderInterface
80
     */
81 8
    public function getHeader(string $name): HeaderInterface
82
    {
83 8
        return $this->decoratedPart->getHeader($name);
84
    }
85
86
    /**
87
     * @param HeaderInterface $header
88
     * @return PartInterface
89
     */
90 1
    public function withHeader(HeaderInterface $header): PartInterface
91
    {
92 1
        throw new \RuntimeException('Cannot modify headers of MultiPart');
93
    }
94
95
    /**
96
     * @param string $name
97
     * @return PartInterface
98
     */
99 1
    public function withoutHeader(string $name): PartInterface
100
    {
101 1
        throw new \RuntimeException('Cannot modify headers of MultiPart');
102
    }
103
104
    /**
105
     * @param StreamInterface $body
106
     * @return PartInterface
107
     */
108 1
    public function withBody(StreamInterface $body): PartInterface
109
    {
110 1
        throw new \RuntimeException('Cannot modify body of MultiPart');
111
    }
112
113
    /**
114
     * @return StreamInterface
115
     */
116 4
    public function getBody(): StreamInterface
117
    {
118 4
        return new EmptyStream();
119
    }
120
121
    /**
122
     * @return Boundary
123
     */
124 6
    public function getBoundary(): Boundary
125
    {
126 6
        return $this->boundary;
127
    }
128
129
    /**
130
     * @param PartInterface $part
131
     * @return MultiPartInterface
132
     */
133 5
    public function withPart(PartInterface $part): MultiPartInterface
134
    {
135 5
        $clone = clone $this;
136 5
        $clone->parts[] = $part;
137 5
        return $clone;
138
    }
139
140
    /**
141
     * @param iterable|PartInterface[] $parts
142
     * @return MultiPartInterface
143
     */
144 4
    public function withParts(iterable $parts): MultiPartInterface
145
    {
146 4
        $clone = clone $this;
147
148 4
        foreach ($parts as $part) {
149 4
            $clone->parts[] = $part;
150
        }
151
152 4
        return $clone;
153
    }
154
155
    /**
156
     * @return iterable|PartInterface[]
157
     */
158 8
    public function getParts(): iterable
159
    {
160 8
        return $this->parts;
161
    }
162
163
    /**
164
     * @param MessageInterface $message
165
     * @return MultiPart
166
     */
167 7
    public static function fromMessage(MessageInterface $message): self
168
    {
169 7
        foreach ($message->getHeader('Content-Type') as $header) {
170 7
            $contentType = $header->getValue()->getRaw();
171 7
            if (\substr($contentType, 0, 10) !== 'multipart/') {
172 7
                throw new \InvalidArgumentException(
173 7
                    \sprintf(
174 7
                        'Message is not a multipart/alternative message, but %s',
175 7
                        $contentType
176
                    )
177
                );
178
            }
179
180
            try {
181 5
                $boundary = new Boundary($header->getValue()->getParameter('boundary')->getValue());
182
            } catch (\UnexpectedValueException $e) {
183
                throw new \InvalidArgumentException('Message does not contain a boundary');
184
            }
185
186 5
            $part = new self(
187 5
                $boundary,
188 5
                new ContentType($header->getValue()->getRaw())
189
            );
190
191 5
            $content = '';
192 5
            $preamble = true;
193 5
            foreach (new LineIterator($message->getBody()) as $line) {
194 5
                if ($boundary->isOpening($line) && $preamble) {
195 5
                    $content = '';
196 5
                    $preamble = false;
197 5
                    continue;
198
                }
199
200 5
                if ($boundary->isClosing($line) || $boundary->isOpening($line)) {
201 5
                    $message = GenericMessage::fromString(\rtrim($content));
202
203
                    try {
204 5
                        $part->parts[] = MultiPart::fromMessage($message);
205 5
                    } catch (\InvalidArgumentException $e) {
206 5
                        $part->parts[] = GenericPart::fromMessage($message);
207
                    }
208
209 5
                    $content = '';
210
                }
211
212 5
                if ($boundary->isOpening($line)) {
213 5
                    continue;
214
                }
215
216 5
                if ($boundary->isClosing($line)) {
217 5
                    break;
218
                }
219
220 5
                $content .= $line . "\r\n";
221
            }
222
223 5
            return $part;
224
        }
225
226
        throw new \InvalidArgumentException('Message is not a multipart/alternative message');
227
    }
228
}
229