Passed
Push — master ( 46ed75...ca2387 )
by Zaahid
03:33
created

MessagePart   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 226
Duplicated Lines 0 %

Test Coverage

Coverage 92.52%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 42
eloc 90
c 4
b 0
f 0
dl 0
loc 226
ccs 99
cts 107
cp 0.9252
rs 9.0399

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getContent() 0 7 2
A detachContentStream() 0 5 1
A getBinaryContentStream() 0 7 3
A setCharsetOverride() 0 6 3
A __toString() 0 3 1
A getContentStream() 0 12 3
A notify() 0 7 3
A getParent() 0 3 1
A __construct() 0 11 2
A hasContent() 0 3 1
A detach() 0 3 1
A getFilename() 0 3 1
A saveContent() 0 17 4
A getStream() 0 3 1
A setContent() 0 6 1
A attach() 0 3 1
A getErrorBagChildren() 0 3 1
A attachContentStream() 0 10 3
A getBinaryContentResourceHandle() 0 7 2
A save() 0 19 4
A getErrorLoggingContextName() 0 10 2
A getResourceHandle() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like MessagePart often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use MessagePart, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * This file is part of the ZBateson\MailMimeParser project.
4
 *
5
 * @license http://opensource.org/licenses/bsd-license.php BSD
6
 */
7
8
namespace ZBateson\MailMimeParser\Message;
9
10
use GuzzleHttp\Psr7\StreamWrapper;
11
use GuzzleHttp\Psr7\Utils;
12
use Psr\Http\Message\StreamInterface;
13
use SplObjectStorage;
14
use SplObserver;
15
16
use ZBateson\MailMimeParser\ErrorBag;
17
use ZBateson\MailMimeParser\MailMimeParser;
18
use ZBateson\MailMimeParser\Stream\StreamFactory;
19
20
/**
21
 * Most basic representation of a single part of an email.
22
 *
23
 * @author Zaahid Bateson
24
 */
25
abstract class MessagePart extends ErrorBag implements IMessagePart
26
{
27
    /**
28
     * @var ?IMimePart parent part
29
     */
30
    protected ?IMimePart $parent;
31
32
    /**
33
     * @var PartStreamContainer holds 'stream' and 'content stream'.
34
     */
35
    protected PartStreamContainer $partStreamContainer;
36
37
    /**
38
     * @var ?string can be used to set an override for content's charset in cases
39
     *      where a user knows the charset on the content is not what it claims
40
     *      to be.
41
     */
42
    protected ?string $charsetOverride = null;
43
44
    /**
45
     * @var bool set to true when a user attaches a stream manually, it's
46
     *      assumed to already be decoded or to have relevant transfer encoding
47
     *      decorators attached already.
48
     */
49
    protected bool $ignoreTransferEncoding = false;
50
51
    /**
52
     * @var SplObjectStorage attached observers that need to be notified of
53
     *      modifications to this part.
54
     */
55
    protected SplObjectStorage $observers;
56
57 153
    public function __construct(?PartStreamContainer $streamContainer = null, ?IMimePart $parent = null)
58
    {
59 153
        parent::__construct();
60 153
        $this->partStreamContainer = $streamContainer;
61 153
        if ($this->partStreamContainer === null) {
62
            $fc = new StreamFactory();
63
            $this->partStreamContainer = new PartStreamContainer($fc);
64
            $this->partStreamContainer->setStream($fc->newMessagePartStream($this));
65
        }
66 153
        $this->parent = $parent;
67 153
        $this->observers = new SplObjectStorage();
68
    }
69
70 118
    public function attach(SplObserver $observer) : void
71
    {
72 118
        $this->observers->attach($observer);
73
    }
74
75 2
    public function detach(SplObserver $observer) : void
76
    {
77 2
        $this->observers->detach($observer);
78
    }
79
80 110
    public function notify() : void
81
    {
82 110
        foreach ($this->observers as $observer) {
83 108
            $observer->update($this);
84
        }
85 110
        if ($this->parent !== null) {
86 70
            $this->parent->notify();
87
        }
88
    }
89
90 25
    public function getParent() : ?IMimePart
91
    {
92 25
        return $this->parent;
93
    }
94
95 111
    public function hasContent() : bool
96
    {
97 111
        return $this->partStreamContainer->hasContent();
98
    }
99
100 1
    public function getFilename() : ?string
101
    {
102 1
        return null;
103
    }
104
105 2
    public function setCharsetOverride(string $charsetOverride, bool $onlyIfNoCharset = false) : static
106
    {
107 2
        if (!$onlyIfNoCharset || $this->getCharset() === null) {
108 2
            $this->charsetOverride = $charsetOverride;
109
        }
110 2
        return $this;
111
    }
112
113 106
    public function getContentStream(string $charset = MailMimeParser::DEFAULT_CHARSET) : ?StreamInterface
114
    {
115 106
        if ($this->hasContent()) {
116 105
            $tr = ($this->ignoreTransferEncoding) ? '' : $this->getContentTransferEncoding();
117 105
            $ch = $this->charsetOverride ?? $this->getCharset();
118 105
            return $this->partStreamContainer->getContentStream(
119 105
                $tr,
120 105
                $ch,
121 105
                $charset
122 105
            );
123
        }
124 50
        return null;
125
    }
126
127 49
    public function getBinaryContentStream() : ?StreamInterface
128
    {
129 49
        if ($this->hasContent()) {
130 49
            $tr = ($this->ignoreTransferEncoding) ? '' : $this->getContentTransferEncoding();
131 49
            return $this->partStreamContainer->getBinaryContentStream($tr);
132
        }
133
        return null;
134
    }
135
136 46
    public function getBinaryContentResourceHandle() : mixed
137
    {
138 46
        $stream = $this->getBinaryContentStream();
139 46
        if ($stream !== null) {
140 46
            return StreamWrapper::getResource($stream);
141
        }
142
        return null;
143
    }
144
145 3
    public function saveContent($filenameResourceOrStream) : static
146
    {
147 3
        $resourceOrStream = $filenameResourceOrStream;
148 3
        if (\is_string($filenameResourceOrStream)) {
149 1
            $resourceOrStream = \fopen($filenameResourceOrStream, 'w+');
150
        }
151
152 3
        $stream = Utils::streamFor($resourceOrStream);
153 3
        Utils::copyToStream($this->getBinaryContentStream(), $stream);
0 ignored issues
show
Bug introduced by
It seems like $this->getBinaryContentStream() can also be of type null; however, parameter $source of GuzzleHttp\Psr7\Utils::copyToStream() does only seem to accept Psr\Http\Message\StreamInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

153
        Utils::copyToStream(/** @scrutinizer ignore-type */ $this->getBinaryContentStream(), $stream);
Loading history...
154
155 3
        if (!\is_string($filenameResourceOrStream)
156 3
            && !($filenameResourceOrStream instanceof StreamInterface)) {
157
            // only detach if it wasn't a string or StreamInterface, so the
158
            // fopen call can be properly closed if it was
159 1
            $stream->detach();
160
        }
161 3
        return $this;
162
    }
163
164 25
    public function getContent(string $charset = MailMimeParser::DEFAULT_CHARSET) : ?string
165
    {
166 25
        $stream = $this->getContentStream($charset);
167 25
        if ($stream !== null) {
168 24
            return $stream->getContents();
169
        }
170 1
        return null;
171
    }
172
173 22
    public function attachContentStream(StreamInterface $stream, string $streamCharset = MailMimeParser::DEFAULT_CHARSET) : static
174
    {
175 22
        $ch = $this->charsetOverride ?? $this->getCharset();
176 22
        if ($ch !== null && $streamCharset !== $ch) {
177 9
            $this->charsetOverride = $streamCharset;
178
        }
179 22
        $this->ignoreTransferEncoding = true;
180 22
        $this->partStreamContainer->setContentStream($stream);
181 22
        $this->notify();
182 22
        return $this;
183
    }
184
185 20
    public function detachContentStream() : static
186
    {
187 20
        $this->partStreamContainer->setContentStream(null);
188 20
        $this->notify();
189 20
        return $this;
190
    }
191
192 18
    public function setContent($resource, string $charset = MailMimeParser::DEFAULT_CHARSET) : static
193
    {
194 18
        $stream = Utils::streamFor($resource);
195 18
        $this->attachContentStream($stream, $charset);
196
        // this->notify() called in attachContentStream
197 18
        return $this;
198
    }
199
200 1
    public function getResourceHandle() : mixed
201
    {
202 1
        return StreamWrapper::getResource($this->getStream());
203
    }
204
205 103
    public function getStream() : StreamInterface
206
    {
207 103
        return $this->partStreamContainer->getStream();
208
    }
209
210 96
    public function save($filenameResourceOrStream, string $filemode = 'w+') : static
211
    {
212 96
        $resourceOrStream = $filenameResourceOrStream;
213 96
        if (\is_string($filenameResourceOrStream)) {
214 1
            $resourceOrStream = \fopen($filenameResourceOrStream, $filemode);
215
        }
216
217 96
        $partStream = $this->getStream();
218 96
        $partStream->rewind();
219 96
        $stream = Utils::streamFor($resourceOrStream);
220 96
        Utils::copyToStream($partStream, $stream);
221
222 96
        if (!\is_string($filenameResourceOrStream)
223 96
            && !($filenameResourceOrStream instanceof StreamInterface)) {
224
            // only detach if it wasn't a string or StreamInterface, so the
225
            // fopen call can be properly closed if it was
226 95
            $stream->detach();
227
        }
228 96
        return $this;
229
    }
230
231 1
    public function __toString() : string
232
    {
233 1
        return $this->getStream()->getContents();
234
    }
235
236 70
    public function getErrorLoggingContextName(): string
237
    {
238 70
        $params = '';
239 70
        if (!empty($this->getContentId())) {
240
            $params .= ', content-id=' . $this->getContentId();
241
        }
242 70
        $params .= ', content-type=' . $this->getContentType();
243 70
        $nsClass = get_class($this);
244 70
        $class = substr($nsClass, (strrpos($nsClass, '\\') ?? -1) + 1);
245 70
        return $class . '(' . spl_object_id($this) . $params . ')';
246
    }
247
248
    protected function getErrorBagChildren() : array
249
    {
250
        return [];
251
    }
252
}
253