Test Failed
Branch 1.0.0 (84f469)
by Zaahid
05:36
created

MimePart::getHeaderParameter()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 2
nop 3
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
namespace ZBateson\MailMimeParser\Message\Part;
8
9
use ZBateson\MailMimeParser\Header\HeaderFactory;
10
use ZBateson\MailMimeParser\Header\ParameterHeader;
11
use ZBateson\MailMimeParser\Message\PartFilterFactory;
12
use ZBateson\MailMimeParser\Message\PartFilter;
13
14
/**
15
 * Represents a single part of a multi-part mime message.
16
 *
17
 * A MimePart object may have any number of child parts, or may be a child
18
 * itself with its own parent or parents.
19
 *
20
 * The content of the part can be read from its PartStream resource handle,
21
 * accessible via MimePart::getContentResourceHanlde.
22
 *
23
 * @author Zaahid Bateson
24
 */
25
class MimePart extends MessagePart
26
{
27
    /**
28
     * @var \ZBateson\MailMimeParser\Header\HeaderFactory the HeaderFactory
29
     *      object used for created headers
30
     */
31
    protected $headerFactory;
32
    
33
    /**
34
     * @var \ZBateson\MailMimeParser\Message\PartFilterFactory factory object
35
     *      responsible for create PartFilters
36
     */
37
    protected $partFilterFactory;
38
39
    /**
40
     * @var \ZBateson\MailMimeParser\Message\Part\MessagePart[] array of child
41
     *      parts
42
     */
43
    protected $children = [];
44
45
    /**
46
     * @var string[][] array of headers, with keys set to lower-cased,
47
     *      alphanumeric characters of the header's name, and values set to an
48
     *      array of 2 elements, the first being the header's original name with
49
     *      non-alphanumeric characters and original case, and the second set to
50
     *      the header's value.
51
     */
52
    protected $rawHeaders;
53
    
54
    /**
55
     * @var \ZBateson\MailMimeParser\Header\AbstractHeader[] array of parsed
56
     * header objects populated on-demand, the key is set to the header's name
57
     * lower-cased, and with non-alphanumeric characters removed.
58
     */
59
    protected $headers;
60
61
    /**
62
     * Sets up class dependencies.
63
     *
64
     * @param HeaderFactory $headerFactory 
65
     * @param PartFilterFactory $partFilterFactory
66
     * @param string $messageObjectId
67
     * @param PartBuilder $partBuilder
68
     * @param PartStreamFilterManager $partStreamFilterManager
69
     */
70
    public function __construct(
71
        HeaderFactory $headerFactory,
72
        PartFilterFactory $partFilterFactory,
73
        $messageObjectId,
74
        PartBuilder $partBuilder,
75
        PartStreamFilterManager $partStreamFilterManager
76
    ) {
77
        parent::__construct($messageObjectId, $partBuilder, $partStreamFilterManager);
78
        $this->headerFactory = $headerFactory;
79
        $this->partFilterFactory = $partFilterFactory;
80
81
        $pbChildren = $partBuilder->getChildren();
82
        if (!empty($pbChildren)) {
83
            $this->children = array_map(function ($child) use ($messageObjectId) {
84
                $childPart = $child->createMessagePart($messageObjectId);
85
                $childPart->parent = $this;
86
                return $childPart;
87
            }, $pbChildren);
88
        }
89
        $this->headers['contenttype'] = $partBuilder->getContentType();
90
        $this->rawHeaders = $partBuilder->getRawHeaders();
91
    }
92
93
    /**
94
     * Returns the part at the given 0-based index, or null if none is set.
95
     * 
96
     * Note that the first part returned is the current part itself.  This is
97
     * often desirable for queries with a PartFilter, e.g. looking for a
98
     * MimePart with a specific Content-Type that may be satisfied by the
99
     * current part.
100
     *
101
     * @param int $index
102
     * @param PartFilter $filter
103
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart
104
     */
105 View Code Duplication
    public function getPart($index, PartFilter $filter = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
    {
107
        $parts = $this->getAllParts($filter);
108
        if (!isset($parts[$index])) {
109
            return null;
110
        }
111
        return $parts[$index];
112
    }
113
114
    /**
115
     * Returns the current part, all child parts, and child parts of all
116
     * children optionally filtering them with the provided PartFilter.
117
     * 
118
     * The first part returned is always the current MimePart.  This is often
119
     * desirable as it may be a valid MimePart for the provided PartFilter.
120
     * 
121
     * @param PartFilter $filter an optional filter
122
     * @return \ZBateson\MailMimeParser\Message\Part\MessagePart[]
123
     */
124
    public function getAllParts(PartFilter $filter = null)
125
    {
126
        $aParts = [ $this ];
127
        foreach ($this->children as $part) {
128
            if ($part instanceof MimePart) {
129
                $aParts = array_merge($aParts, $part->getAllParts(null, true));
0 ignored issues
show
Unused Code introduced by
The call to MimePart::getAllParts() has too many arguments starting with true.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
130
            } else {
131
                array_push($aParts, $part);
132
            }
133
        }
134
        if (!empty($filter)) {
135
            return array_values(array_filter(
136
                $aParts,
137
                [ $filter, 'filter' ]
138
            ));
139
        }
140
        return $aParts;
141
    }
142
143
    /**
144
     * Returns the total number of parts in this and all children.
145
     * 
146
     * Note that the current part is considered, so the minimum getPartCount is
147
     * 1 without a filter.
148
     *
149
     * @param PartFilter $filter
150
     * @return int
151
     */
152
    public function getPartCount(PartFilter $filter = null)
153
    {
154
        return count($this->getAllParts($filter));
155
    }
156
    
157
    /**
158
     * Returns the direct child at the given 0-based index, or null if none is
159
     * set.
160
     *
161
     * @param int $index
162
     * @param PartFilter $filter
163
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart
164
     */
165 View Code Duplication
    public function getChild($index, PartFilter $filter = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
166
    {
167
        $parts = $this->getChildParts($filter);
168
        if (!isset($parts[$index])) {
169
            return null;
170
        }
171
        return $parts[$index];
172
    }
173
    
174
    /**
175
     * Returns all direct child parts.
176
     * 
177
     * If a PartFilter is provided, the PartFilter is applied before returning.
178
     * 
179
     * @param PartFilter $filter
180
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart[]
181
     */
182
    public function getChildParts(PartFilter $filter = null)
183
    {
184
        if ($filter !== null) {
185
            return array_values(array_filter($this->children, [ $filter, 'filter' ]));
186
        }
187
        return $this->children;
188
    }
189
    
190
    /**
191
     * Returns the number of direct children under this part.
192
     * 
193
     * @param PartFilter $filter
194
     * @return int
195
     */
196
    public function getChildCount(PartFilter $filter = null)
197
    {
198
        return count($this->getChildParts($filter));
199
    }
200
201
    /**
202
     * Returns the part associated with the passed mime type if it exists.
203
     *
204
     * @param string $mimeType
205
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart or null
206
     */
207
    public function getPartByMimeType($mimeType, $index = 0)
208
    {
209
        $partFilter = $this->partFilterFactory->newFilterFromContentType($mimeType);
210
        return $this->getPart($index, $partFilter);
211
    }
212
    
213
    /**
214
     * Returns an array of all parts associated with the passed mime type if any
215
     * exist or null otherwise.
216
     *
217
     * @param string $mimeType
218
     * @return \ZBateson\MailMimeParser\Message\Part\MimePart[] or null
219
     */
220
    public function getAllPartsByMimeType($mimeType)
221
    {
222
        $partFilter = $this->partFilterFactory->newFilterFromContentType($mimeType);
223
        return $this->getAllParts($partFilter);
224
    }
225
    
226
    /**
227
     * Returns the number of parts matching the passed $mimeType
228
     * 
229
     * @param string $mimeType
230
     * @return int
231
     */
232
    public function getCountOfPartsByMimeType($mimeType)
233
    {
234
        $partFilter = $this->partFilterFactory->newFilterFromContentType($mimeType);
235
        return $this->getPartCount($partFilter);
236
    }
237
238
    /**
239
     * Returns true if this part's mime type is multipart/*
240
     *
241
     * @return bool
242
     */
243
    public function isMultiPart()
244
    {
245
        // casting to bool, preg_match returns 1 for true
246
        return (bool) (preg_match(
247
            '~multipart/\w+~i',
248
            $this->getContentType()
249
        ));
250
    }
251
    
252
    /**
253
     * Returns a filename for the part if one is defined, or null otherwise.
254
     * 
255
     * @return string
256
     */
257
    public function getFilename()
258
    {
259
        return $this->getHeaderParameter('Content-Disposition', 'filename');
260
    }
261
    
262
    /**
263
     * Returns true.
264
     * 
265
     * @return bool
266
     */
267
    public function isMime()
268
    {
269
        return true;
270
    }
271
    
272
    /**
273
     * Returns true if this part's mime type is text/plain, text/html or if the
274
     * Content-Type header defines a charset.
275
     * 
276
     * @return bool
277
     */
278
    public function isTextPart()
279
    {
280
        return ($this->getCharset() !== null);
281
    }
282
    
283
    /**
284
     * Returns the lower-cased, trimmed value of the Content-Type header.
285
     * 
286
     * Parses the Content-Type header, defaults to returning text/plain if not
287
     * defined.
288
     * 
289
     * @return string
290
     */
291
    public function getContentType($default = 'text/plain')
292
    {
293
        return trim(strtolower($this->getHeaderValue('Content-Type', $default)));
294
    }
295
    
296
    /**
297
     * Returns the upper-cased charset of the Content-Type header's charset
298
     * parameter if set, US-ASCII if the Content-Type is text/plain or text/html
299
     * and the charset parameter isn't set, or null otherwise.
300
     * 
301
     * @return string
302
     */
303
    public function getCharset()
304
    {
305
        $charset = $this->getHeaderParameter('Content-Type', 'charset');
306
        if ($charset === null) {
307
            $contentType = $this->getContentType();
308
            if ($contentType === 'text/plain' || $contentType === 'text/html') {
309
                return 'US-ASCII';
310
            }
311
            return null;
312
        }
313
        return trim(strtoupper($charset));
314
    }
315
    
316
    /**
317
     * Returns the content's disposition, defaulting to 'inline' if not set.
318
     * 
319
     * @return string
320
     */
321
    public function getContentDisposition($default = 'inline')
322
    {
323
        return strtolower($this->getHeaderValue('Content-Disposition', $default));
324
    }
325
    
326
    /**
327
     * Returns the content-transfer-encoding used for this part, defaulting to
328
     * '7bit' if not set.
329
     * 
330
     * @return string
331
     */
332
    public function getContentTransferEncoding($default = '7bit')
333
    {
334
        return strtolower($this->getHeaderValue('Content-Transfer-Encoding', $default));
335
    }
336
337
    /**
338
     * Returns the AbstractHeader object for the header with the given $name
339
     *
340
     * Note that mime headers aren't case sensitive.
341
     *
342
     * @param string $name
343
     * @return \ZBateson\MailMimeParser\Header\AbstractHeader
344
     */
345
    public function getHeader($name)
346
    {
347
        $nameKey = preg_replace('/[^a-z0-9]/', '', strtolower($name));
348
        if (isset($this->rawHeaders[$nameKey])) {
349
            if (!isset($this->headers[$nameKey])) {
350
                $this->headers[$nameKey] = $this->headerFactory->newInstance(
351
                    $this->rawHeaders[$nameKey][0],
352
                    $this->rawHeaders[$nameKey][1]
353
                );
354
            }
355
            return $this->headers[$nameKey];
356
        }
357
        return null;
358
    }
359
360
    /**
361
     * Returns the string value for the header with the given $name.
362
     *
363
     * Note that mime headers aren't case sensitive.
364
     *
365
     * @param string $name
366
     * @param string $defaultValue
367
     * @return string
368
     */
369
    public function getHeaderValue($name, $defaultValue = null)
370
    {
371
        $header = $this->getHeader($name);
372
        if ($header !== null) {
373
            return $header->getValue();
374
        }
375
        return $defaultValue;
376
    }
377
378
    /**
379
     * Returns a parameter of the header $header, given the parameter named
380
     * $param.
381
     *
382
     * Only headers of type
383
     * \ZBateson\MailMimeParser\Header\ParameterHeader have parameters.
384
     * Content-Type and Content-Disposition are examples of headers with
385
     * parameters. "Charset" is a common parameter of Content-Type.
386
     *
387
     * @param string $header
388
     * @param string $param
389
     * @param string $defaultValue
390
     * @return string
391
     */
392
    public function getHeaderParameter($header, $param, $defaultValue = null)
393
    {
394
        $obj = $this->getHeader($header);
395
        if ($obj && $obj instanceof ParameterHeader) {
396
            return $obj->getValueFor($param, $defaultValue);
397
        }
398
        return $defaultValue;
399
    }
400
}
401