Passed
Push — master ( 181d77...7fe46a )
by Zaahid
06:13 queued 12s
created

MessagePart   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Test Coverage

Coverage 97.83%

Importance

Changes 4
Bugs 0 Features 0
Metric Value
wmc 38
eloc 77
c 4
b 0
f 0
dl 0
loc 218
ccs 90
cts 92
cp 0.9783
rs 9.36

20 Methods

Rating   Name   Duplication   Size   Complexity  
A notify() 0 7 3
A getParent() 0 3 1
A __construct() 0 5 1
A hasContent() 0 3 1
A detach() 0 3 1
A getFilename() 0 3 1
A attach() 0 3 1
A getBinaryContentStream() 0 7 3
A setCharsetOverride() 0 6 3
A getContentStream() 0 12 3
A saveContent() 0 17 4
A getContent() 0 7 2
A getBinaryContentResourceHandle() 0 7 2
A __toString() 0 3 1
A getStream() 0 3 1
A setContent() 0 6 1
A attachContentStream() 0 10 3
A save() 0 19 4
A detachContentStream() 0 5 1
A getResourceHandle() 0 3 1
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
use ZBateson\MailMimeParser\MailMimeParser;
16
17
/**
18
 * Most basic representation of a single part of an email.
19
 *
20
 * @author Zaahid Bateson
21
 */
22
abstract class MessagePart implements IMessagePart
23
{
24
    /**
25
     * @var ?IMimePart parent part
26
     */
27
    protected $parent;
28
29
    /**
30
     * @var PartStreamContainer holds 'stream' and 'content stream'.
31
     */
32
    protected $partStreamContainer;
33
34
    /**
35
     * @var ?string can be used to set an override for content's charset in cases
36
     *      where a user knows the charset on the content is not what it claims
37
     *      to be.
38
     */
39
    protected $charsetOverride;
40
41
    /**
42
     * @var bool set to true when a user attaches a stream manually, it's
43
     *      assumed to already be decoded or to have relevant transfer encoding
44
     *      decorators attached already.
45
     */
46
    protected $ignoreTransferEncoding;
47
48
    /**
49
     * @var SplObjectStorage attached observers that need to be notified of
50
     *      modifications to this part.
51
     */
52
    protected $observers;
53
54 153
    public function __construct(PartStreamContainer $streamContainer, ?IMimePart $parent = null)
55
    {
56 153
        $this->partStreamContainer = $streamContainer;
57 153
        $this->parent = $parent;
58 153
        $this->observers = new SplObjectStorage();
59
    }
60
61 117
    public function attach(SplObserver $observer) : void
62
    {
63 117
        $this->observers->attach($observer);
64
    }
65
66 4
    public function detach(SplObserver $observer) : void
67
    {
68 4
        $this->observers->detach($observer);
69
    }
70
71 109
    public function notify() : void
72
    {
73 109
        foreach ($this->observers as $observer) {
74 107
            $observer->update($this);
75
        }
76 109
        if ($this->parent !== null) {
77 69
            $this->parent->notify();
78
        }
79
    }
80
81 24
    public function getParent()
82
    {
83 24
        return $this->parent;
84
    }
85
86 110
    public function hasContent() : bool
87
    {
88 110
        return $this->partStreamContainer->hasContent();
89
    }
90
91 1
    public function getFilename() : ?string
92
    {
93 1
        return null;
94
    }
95
96
    /**
97
     * @return static
98
     */
99 2
    public function setCharsetOverride(string $charsetOverride, bool $onlyIfNoCharset = false)
100
    {
101 2
        if (!$onlyIfNoCharset || $this->getCharset() === null) {
102 2
            $this->charsetOverride = $charsetOverride;
103
        }
104 2
        return $this;
105
    }
106
107 105
    public function getContentStream(string $charset = MailMimeParser::DEFAULT_CHARSET)
108
    {
109 105
        if ($this->hasContent()) {
110 104
            $tr = ($this->ignoreTransferEncoding) ? '' : $this->getContentTransferEncoding();
111 104
            $ch = $this->charsetOverride ?? $this->getCharset();
112 104
            return $this->partStreamContainer->getContentStream(
113 104
                $tr,
114 104
                $ch,
115 104
                $charset
116 104
            );
117
        }
118 47
        return null;
119
    }
120
121 49
    public function getBinaryContentStream()
122
    {
123 49
        if ($this->hasContent()) {
124 49
            $tr = ($this->ignoreTransferEncoding) ? '' : $this->getContentTransferEncoding();
125 49
            return $this->partStreamContainer->getBinaryContentStream($tr);
126
        }
127
        return null;
128
    }
129
130 46
    public function getBinaryContentResourceHandle()
131
    {
132 46
        $stream = $this->getBinaryContentStream();
133 46
        if ($stream !== null) {
134 46
            return StreamWrapper::getResource($stream);
135
        }
136
        return null;
137
    }
138
139 3
    public function saveContent($filenameResourceOrStream) : self
140
    {
141 3
        $resourceOrStream = $filenameResourceOrStream;
142 3
        if (\is_string($filenameResourceOrStream)) {
143 1
            $resourceOrStream = \fopen($filenameResourceOrStream, 'w+');
144
        }
145
146 3
        $stream = Utils::streamFor($resourceOrStream);
147 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

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