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

MimePart   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 289
Duplicated Lines 0 %

Test Coverage

Coverage 94.12%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 39
eloc 77
c 2
b 0
f 0
dl 0
loc 289
ccs 96
cts 102
cp 0.9412
rs 9.28

22 Methods

Rating   Name   Duplication   Size   Complexity  
A isMime() 0 3 1
A isMultiPart() 0 6 1
A getRawHeaderIterator() 0 3 1
A isTextPart() 0 3 1
A isSignaturePart() 0 6 3
A getFilename() 0 8 1
A getHeader() 0 3 1
A getContentDisposition() 0 7 3
A getHeaderValue() 0 7 3
A getContentTransferEncoding() 0 12 2
A __construct() 0 24 5
A getContentId() 0 3 1
A getCharset() 0 11 5
A getHeaderParameter() 0 7 3
A getContentType() 0 3 1
A getAllHeadersByName() 0 3 1
A getAllHeaders() 0 3 1
A getRawHeaders() 0 3 1
A removeHeader() 0 5 1
A setRawHeader() 0 5 1
A addRawHeader() 0 5 1
A removeSingleHeader() 0 5 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 ZBateson\MailMimeParser\Header\HeaderConsts;
11
use ZBateson\MailMimeParser\Header\ParameterHeader;
12
use ZBateson\MailMimeParser\IMessage;
13
use ZBateson\MailMimeParser\MailMimeParser;
14
15
/**
16
 * A mime email message part.
17
 *
18
 * A MIME part may contain any combination of headers, content and children.
19
 *
20
 * @author Zaahid Bateson
21
 */
22
class MimePart extends MultiPart implements IMimePart
23
{
24
    /**
25
     * @var PartHeaderContainer Container for this part's headers.
26
     */
27
    protected $headerContainer;
28
29 138
    public function __construct(
30
        ?IMimePart $parent = null,
31
        ?PartStreamContainer $streamContainer = null,
32
        ?PartHeaderContainer $headerContainer = null,
33
        ?PartChildrenContainer $partChildrenContainer = null
34
    ) {
35 138
        $setStream = false;
36 138
        $di = MailMimeParser::getDependencyContainer();
37 138
        if ($streamContainer === null || $headerContainer === null || $partChildrenContainer === null) {
38
            $headerContainer = $di[\ZBateson\MailMimeParser\Message\PartHeaderContainer::class];
39
            $streamContainer = $di[\ZBateson\MailMimeParser\Message\PartStreamContainer::class];
40
            $partChildrenContainer = $di[\ZBateson\MailMimeParser\Message\PartChildrenContainer::class];
41
            $setStream = true;
42
        }
43 138
        parent::__construct(
44 138
            $parent,
45 138
            $streamContainer,
46 138
            $partChildrenContainer
47 138
        );
48 138
        if ($setStream) {
49
            $streamFactory = $di[\ZBateson\MailMimeParser\Stream\StreamFactory::class];
50
            $streamContainer->setStream($streamFactory->newMessagePartStream($this));
51
        }
52 138
        $this->headerContainer = $headerContainer;
53
    }
54
55
    /**
56
     * Returns a filename for the part if one is defined, or null otherwise.
57
     *
58
     * Uses the 'filename' parameter of the Content-Disposition header if it
59
     * exists, or the 'name' parameter of the 'Content-Type' header if it
60
     * doesn't.
61
     *
62
     * @return string|null the file name of the part or null.
63
     */
64 56
    public function getFilename() : ?string
65
    {
66 56
        return $this->getHeaderParameter(
67 56
            HeaderConsts::CONTENT_DISPOSITION,
68 56
            'filename',
69 56
            $this->getHeaderParameter(
70 56
                HeaderConsts::CONTENT_TYPE,
71 56
                'name'
72 56
            )
73 56
        );
74
    }
75
76
    /**
77
     * Returns true.
78
     *
79
     */
80 2
    public function isMime() : bool
81
    {
82 2
        return true;
83
    }
84
85 57
    public function isMultiPart()
86
    {
87
        // casting to bool, preg_match returns 1 for true
88 57
        return (bool) (\preg_match(
89 57
            '~multipart/.*~i',
90 57
            $this->getContentType()
91 57
        ));
92
    }
93
94
    /**
95
     * Returns true if this part has a defined 'charset' on its Content-Type
96
     * header.
97
     *
98
     * This may result in some false positives if charset is set on a part that
99
     * is not plain text which has been seen.  If a part is known to be binary,
100
     * it's better to use {@see IMessagePart::getBinaryContentStream()} to
101
     * avoid issues, or to call {@see IMessagePart::saveContent()} directly if
102
     * saving a part's content.
103
     *
104
     */
105 48
    public function isTextPart() : bool
106
    {
107 48
        return ($this->getCharset() !== null);
108
    }
109
110
    /**
111
     * Returns the mime type of the content, or $default if one is not set.
112
     *
113
     * Looks at the part's Content-Type header and returns its value if set, or
114
     * defaults to 'text/plain'.
115
     *
116
     * Note that the returned value is converted to lower case, and may not be
117
     * identical to calling {@see MimePart::getHeaderValue('Content-Type')} in
118
     * some cases.
119
     *
120
     * @param string $default Optional default value to specify a default other
121
     *        than text/plain if needed.
122
     * @return string the mime type
123
     */
124 108
    public function getContentType(string $default = 'text/plain') : ?string
125
    {
126 108
        return \strtolower($this->getHeaderValue(HeaderConsts::CONTENT_TYPE, $default));
127
    }
128
129
    /**
130
     * Returns the charset of the content, or null if not applicable/defined.
131
     *
132
     * Looks for a 'charset' parameter under the 'Content-Type' header of this
133
     * part and returns it if set, defaulting to 'ISO-8859-1' if the
134
     * Content-Type header exists and is of type text/plain or text/html.
135
     *
136
     * Note that the returned value is also converted to upper case.
137
     *
138
     * @return string|null the charset
139
     */
140 104
    public function getCharset() : ?string
141
    {
142 104
        $charset = $this->getHeaderParameter(HeaderConsts::CONTENT_TYPE, 'charset');
143 104
        if ($charset === null || \strcasecmp($charset, 'binary') === 0) {
144 76
            $contentType = $this->getContentType();
145 76
            if ($contentType === 'text/plain' || $contentType === 'text/html') {
146 11
                return 'ISO-8859-1';
147
            }
148 68
            return null;
149
        }
150 84
        return \strtoupper($charset);
151
    }
152
153
    /**
154
     * Returns the content's disposition, or returns the value of $default if
155
     * not defined.
156
     *
157
     * Looks at the 'Content-Disposition' header, which should only contain
158
     * either 'inline' or 'attachment'.  If the header is not one of those
159
     * values, $default is returned, which defaults to 'inline' unless passed
160
     * something else.
161
     *
162
     * @param string $default Optional default value if not set or does not
163
     *        match 'inline' or 'attachment'.
164
     * @return string the content disposition
165
     */
166 97
    public function getContentDisposition(?string $default = 'inline') : ?string
167
    {
168 97
        $value = $this->getHeaderValue(HeaderConsts::CONTENT_DISPOSITION);
169 97
        if ($value === null || !\in_array($value, ['inline', 'attachment'])) {
170 95
            return $default;
171
        }
172 55
        return \strtolower($value);
173
    }
174
175
    /**
176
     * Returns the content transfer encoding used to encode the content on this
177
     * part, or the value of $default if not defined.
178
     *
179
     * Looks up and returns the value of the 'Content-Transfer-Encoding' header
180
     * if set, defaulting to '7bit' if an alternate $default param is not
181
     * passed.
182
     *
183
     * The returned value is always lowercase, and header values of 'x-uue',
184
     * 'uue' and 'uuencode' will return 'x-uuencode' instead.
185
     *
186
     * @param string $default Optional default value to return if the header
187
     *        isn't set.
188
     * @return string the content transfer encoding.
189
     */
190 103
    public function getContentTransferEncoding(?string $default = '7bit') : ?string
191
    {
192 103
        static $translated = [
193 103
            'x-uue' => 'x-uuencode',
194 103
            'uue' => 'x-uuencode',
195 103
            'uuencode' => 'x-uuencode'
196 103
        ];
197 103
        $type = \strtolower($this->getHeaderValue(HeaderConsts::CONTENT_TRANSFER_ENCODING, $default));
0 ignored issues
show
Bug introduced by
It seems like $this->getHeaderValue(ZB...FER_ENCODING, $default) can also be of type null; however, parameter $string of strtolower() does only seem to accept string, 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

197
        $type = \strtolower(/** @scrutinizer ignore-type */ $this->getHeaderValue(HeaderConsts::CONTENT_TRANSFER_ENCODING, $default));
Loading history...
198 103
        if (isset($translated[$type])) {
199 2
            return $translated[$type];
200
        }
201 103
        return $type;
202
    }
203
204
    /**
205
     * Returns the Content ID of the part, or null if not defined.
206
     *
207
     * Looks up and returns the value of the 'Content-ID' header.
208
     *
209
     * @return string|null the content ID or null if not defined.
210
     */
211 3
    public function getContentId() : ?string
212
    {
213 3
        return $this->getHeaderValue(HeaderConsts::CONTENT_ID);
214
    }
215
216
    /**
217
     * Returns true if this part's parent is an IMessage, and is the same part
218
     * returned by {@see IMessage::getSignaturePart()}.
219
     *
220
     * @return bool
221
     */
222 57
    public function isSignaturePart()
223
    {
224 57
        if ($this->parent === null || !$this->parent instanceof IMessage) {
225 20
            return false;
226
        }
227 52
        return $this->parent->getSignaturePart() === $this;
228
    }
229
230 118
    public function getHeader($name, $offset = 0)
231
    {
232 118
        return $this->headerContainer->get($name, $offset);
233
    }
234
235 19
    public function getAllHeaders()
236
    {
237 19
        return $this->headerContainer->getHeaderObjects();
238
    }
239
240 1
    public function getAllHeadersByName($name)
241
    {
242 1
        return $this->headerContainer->getAll($name);
243
    }
244
245 1
    public function getRawHeaders()
246
    {
247 1
        return $this->headerContainer->getHeaders();
248
    }
249
250 93
    public function getRawHeaderIterator()
251
    {
252 93
        return $this->headerContainer->getIterator();
253
    }
254
255 115
    public function getHeaderValue($name, $defaultValue = null)
256
    {
257 115
        $header = $this->getHeader($name);
258 115
        if ($header !== null) {
259 114
            return $header->getValue() ?: $defaultValue;
260
        }
261 105
        return $defaultValue;
262
    }
263
264 107
    public function getHeaderParameter($header, $param, $defaultValue = null)
265
    {
266 107
        $obj = $this->getHeader($header);
267 107
        if ($obj && $obj instanceof ParameterHeader) {
268 103
            return $obj->getValueFor($param, $defaultValue);
269
        }
270 13
        return $defaultValue;
271
    }
272
273
    /**
274
     * @return static
275
     */
276 22
    public function setRawHeader(string $name, ?string $value, int $offset = 0)
277
    {
278 22
        $this->headerContainer->set($name, $value, $offset);
279 22
        $this->notify();
280 22
        return $this;
281
    }
282
283
    /**
284
     * @return static
285
     */
286 1
    public function addRawHeader(string $name, string $value)
287
    {
288 1
        $this->headerContainer->add($name, $value);
289 1
        $this->notify();
290 1
        return $this;
291
    }
292
293
    /**
294
     * @return static
295
     */
296 18
    public function removeHeader(string $name)
297
    {
298 18
        $this->headerContainer->removeAll($name);
299 18
        $this->notify();
300 18
        return $this;
301
    }
302
303
    /**
304
     * @return static
305
     */
306 1
    public function removeSingleHeader(string $name, int $offset = 0)
307
    {
308 1
        $this->headerContainer->remove($name, $offset);
309 1
        $this->notify();
310 1
        return $this;
311
    }
312
}
313