Completed
Push — master ( 74fe50...0f19ea )
by Zaahid
09:04
created

MimePart::isTextPart()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 12
ccs 9
cts 9
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 3
nop 0
crap 4
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;
8
9
use ZBateson\MailMimeParser\Header\HeaderFactory;
10
use ZBateson\MailMimeParser\Header\ParameterHeader;
11
use ZBateson\MailMimeParser\Message\Writer\MimePartWriter;
12
13
/**
14
 * Represents a single part of a multi-part mime message.
15
 *
16
 * A MimePart object may have any number of child parts, or may be a child
17
 * itself with its own parent or parents.
18
 *
19
 * The content of the part can be read from its PartStream resource handle,
20
 * accessible via MimePart::getContentResourceHanlde.
21
 *
22
 * @author Zaahid Bateson
23
 */
24
class MimePart
25
{
26
    /**
27
     * @var \ZBateson\MailMimeParser\Header\HeaderFactory the HeaderFactory
28
     *      object used for created headers
29
     */
30
    protected $headerFactory;
31
32
    /**
33
     * @var \ZBateson\MailMimeParser\Header\AbstractHeader[] array of header
34
     * objects
35
     */
36
    protected $headers;
37
38
    /**
39
     * @var \ZBateson\MailMimeParser\Message\MimePart parent part
40
     */
41
    protected $parent;
42
43
    /**
44
     * @var resource the content's resource handle
45
     */
46
    protected $handle;
47
48
    /**
49
     * @var \ZBateson\MailMimeParser\Message\MimePart[] array of parts in this
50
     *      message
51
     */
52
    protected $parts = [];
53
54
    /**
55
     * @var \ZBateson\MailMimeParser\Message\MimePart[][] Maps mime types to
56
     * parts for looking up in getPartByMimeType
57
     */
58
    protected $mimeToPart = [];
59
    
60
    /**
61
     * @var \ZBateson\MailMimeParser\Message\Writer\MimePartWriter the part
62
     *      writer for this MimePart
63
     */
64
    protected $partWriter = null;
65
66
    /**
67
     * Sets up class dependencies.
68
     *
69
     * @param HeaderFactory $headerFactory
70
     * @param MimePartWriter $partWriter
71
     */
72 91
    public function __construct(HeaderFactory $headerFactory, MimePartWriter $partWriter)
73
    {
74 91
        $this->headerFactory = $headerFactory;
75 91
        $this->partWriter = $partWriter;
76 91
    }
77
78
    /**
79
     * Closes the attached resource handle.
80
     */
81 91
    public function __destruct()
82
    {
83 91
        if (is_resource($this->handle)) {
84 10
            fclose($this->handle);
85 10
        }
86 91
    }
87
88
    /**
89
     * Adds the passed part to the parts array, and registers non-attachment/
90
     * non-multipart parts by their content type.
91
     * 
92
     * If the $position parameter is non-null, adds the part at the passed
93
     * position index.
94
     *
95
     * @param \ZBateson\MailMimeParser\Message\MimePart $part
96
     * @param int $position
97
     */
98 83
    public function addPart(MimePart $part, $position = null)
99
    {
100 83
        if ($part->getParent() !== null && $this !== $part->getParent()) {
101 24
            $part->getParent()->addPart($part, $position);
102 83
        } elseif ($part !== $this) {
103 59
            array_splice($this->parts, ($position === null) ? count($this->parts) : $position, 0, [ $part ]);
104 59
        }
105 83
        $this->registerPart($part);
106 83
    }
107
    
108
    /**
109
     * Registers non-attachment parts in the mime type registry
110
     * 
111
     * @param \ZBateson\MailMimeParser\Message\MimePart $part
112
     */
113 83
    protected function registerPart(MimePart $part)
114
    {
115 83
        if ($part->getHeaderValue('Content-Disposition') === null && !$part->isMultiPart()) {
116 79
            $key = strtolower($part->getHeaderValue('Content-Type', 'text/plain'));
117 79
            if (!isset($this->mimeToPart[$key])) {
118 79
                $this->mimeToPart[$key] = [];
119 79
            }
120 79
            $this->mimeToPart[$key][] = $part;
121 79
        }
122 83
    }
123
    
124
    /**
125
     * Removes the part from the mime-type registry.
126
     * 
127
     * @param \ZBateson\MailMimeParser\Message\MimePart $part
128
     */
129 14
    protected function unregisterPart(MimePart $part)
130
    {
131 14
        $key = strtolower($part->getHeaderValue('Content-Type', 'text/plain'));
132 14
        if (isset($this->mimeToPart[$key])) {
133 11
            foreach ($this->mimeToPart[$key] as $index => $p) {
134 11
                if ($p === $part) {
135 11
                    array_splice($this->mimeToPart[$key], $index, 1);
136 11
                    break;
137
                }
138 11
            }
139 11
        }
140 14
    }
141
    
142
    /**
143
     * Unregisters the child part from this part and returns its position or
144
     * null if it wasn't found.
145
     *
146
     * @param \ZBateson\MailMimeParser\Message\MimePart $part
147
     * @return int or null if not found
148
     */
149 14
    public function removePart(MimePart $part)
150
    {
151 14
        $parent = $part->getParent();
152 14
        $this->unregisterPart($part);
153 14
        if ($this !== $parent && $parent !== null) {
154 1
            return $parent->removePart($part);
155
        } else {
156 14
            $position = array_search($part, $this->parts, true);
157 14
            if ($position !== false) {
158 11
                array_splice($this->parts, $position, 1);
159 11
                return $position;
160
            }
161
        }
162 4
        return null;
163
    }
164
165
    /**
166
     * Returns the part at the given 0-based index, or null if none is set.
167
     *
168
     * @param int $index
169
     * @return \ZBateson\MailMimeParser\Message\MimePart
170
     */
171 2
    public function getPart($index)
172
    {
173 2
        $parts = $this->getAllParts();
174 2
        if (!isset($parts[$index])) {
175
            return null;
176
        }
177 2
        return $parts[$index];
178
    }
179
180
    /**
181
     * Returns all child parts, and child parts of all children.
182
     * 
183
     * @return \ZBateson\MailMimeParser\Message\MimePart[]
184
     */
185 11
    public function getAllParts()
186
    {
187 11
        $aParts = [];
188 11
        foreach ($this->parts as $part) {
189 11
            $aParts = array_merge($aParts, [ $part ], $part->getAllParts());
190 11
        }
191 11
        return $aParts;
192
    }
193
194
    /**
195
     * Returns the total number of parts in this and all children.
196
     *
197
     * @return int
198
     */
199 1
    public function getPartCount()
200
    {
201 1
        return count($this->parts) + array_sum(
202 1
            array_map(function ($part) {
203 1
                return $part->getPartCount();
204 1
            },
205 1
            $this->parts)
206 1
        );
207
    }
208
    
209
    /**
210
     * Returns the direct child at the given 0-based index, or null if none is
211
     * set.
212
     *
213
     * @param int $index
214
     * @return \ZBateson\MailMimeParser\Message\MimePart
215
     */
216 9
    public function getChild($index)
217
    {
218 9
        if (!isset($this->parts[$index])) {
219
            return null;
220
        }
221 9
        return $this->parts[$index];
222
    }
223
    
224
    /**
225
     * Returns all direct child parts.
226
     * 
227
     * @return \ZBateson\MailMimeParser\Message\MimePart[]
228
     */
229 80
    public function getChildParts()
230
    {
231 80
        return $this->parts;
232
    }
233
    
234
    /**
235
     * Returns the number of direct children under this part.
236
     * 
237
     * @return \ZBateson\MailMimeParser\Message\MimePart[]
238
     */
239 1
    public function getChildCount()
240
    {
241 1
        return count($this->parts);
242
    }
243
244
    /**
245
     * Returns the part associated with the passed mime type if it exists.
246
     *
247
     * @param string $mimeType
248
     * @return \ZBateson\MailMimeParser\Message\MimePart or null
249
     */
250 22 View Code Duplication
    public function getPartByMimeType($mimeType)
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...
251
    {
252 22
        $key = strtolower($mimeType);
253 22
        if (isset($this->mimeToPart[$key])) {
254 22
            return $this->mimeToPart[$key][0];
255
        }
256
        return null;
257
    }
258
    
259
    /**
260
     * Returns an array of all parts associated with the passed mime type if any
261
     * exist or null otherwise.
262
     *
263
     * @param string $mimeType
264
     * @return \ZBateson\MailMimeParser\Message\MimePart[] or null
265
     */
266 View Code Duplication
    public function getAllPartsByMimeType($mimeType)
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...
267
    {
268
        $key = strtolower($mimeType);
269
        if (isset($this->mimeToPart[$key])) {
270
            return $this->mimeToPart[$key];
271
        }
272
        return null;
273
    }
274
275
    /**
276
     * Returns true if there's a content stream associated with the part.
277
     *
278
     * @return boolean
279
     */
280 16
    public function hasContent()
281
    {
282 16
        if ($this->handle !== null) {
283 16
            return true;
284
        }
285 1
        return false;
286
    }
287
288
    /**
289
     * Returns true if this part's mime type is multipart/*
290
     *
291
     * @return bool
292
     */
293 83
    public function isMultiPart()
294
    {
295 83
        return preg_match(
296 83
            '~multipart/\w+~i',
297 83
            $this->getHeaderValue('Content-Type', 'text/plain')
298 83
        );
299
    }
300
    
301
    /**
302
     * Returns true if this part's mime type is text/plain, text/html or has a
303
     * text/* and has a defined 'charset' attribute.
304
     * 
305
     * @return bool
306
     */
307 83
    public function isTextPart()
308
    {
309 83
        $type = $this->getHeaderValue('Content-Type', 'text/plain');
310 83
        if ($type === 'text/html' || $type === 'text/plain') {
311 72
            return true;
312
        }
313 61
        $charset = $this->getHeaderParameter('Content-Type', 'charset');
314 61
        return ($charset !== null && preg_match(
315 4
            '~text/\w+~i',
316 4
            $this->getHeaderValue('Content-Type', 'text/plain')
317 61
        ));
318
    }
319
320
    /**
321
     * Attaches the resource handle for the part's content.  The attached handle
322
     * is closed when the MimePart object is destroyed.
323
     *
324
     * @param resource $contentHandle
325
     */
326 85
    public function attachContentResourceHandle($contentHandle)
327
    {
328 85
        if ($this->handle !== null && $this->handle !== $contentHandle) {
329 11
            fclose($this->handle);
330 11
        }
331 85
        $this->handle = $contentHandle;
332 85
    }
333
334
    /**
335
     * Detaches the content resource handle from this part but does not close
336
     * it.
337
     */
338 14
    protected function detachContentResourceHandle()
339
    {
340 14
        $this->handle = null;
341 14
    }
342
343
    /**
344
     * Sets the content of the part to the passed string (effectively creates
345
     * a php://temp stream with the passed content and calls
346
     * attachContentResourceHandle with the opened stream).
347
     *
348
     * @param string $string
349
     */
350 9
    public function setContent($string)
351
    {
352 9
        $handle = fopen('php://temp', 'r+');
353 9
        fwrite($handle, $string);
354 9
        rewind($handle);
355 9
        $this->attachContentResourceHandle($handle);
356 9
    }
357
358
    /**
359
     * Returns the resource stream handle for the part's content or null if not
360
     * set.  rewind() is called on the stream before returning it.
361
     *
362
     * The resource is automatically closed by MimePart's destructor and should
363
     * not be closed otherwise.
364
     *
365
     * @return resource
366
     */
367 84
    public function getContentResourceHandle()
368
    {
369 84
        if (is_resource($this->handle)) {
370 84
            rewind($this->handle);
371 84
        }
372 84
        return $this->handle;
373
    }
374
375
    /**
376
     * Shortcut to reading stream content and assigning it to a string.  Returns
377
     * null if the part doesn't have a content stream.
378
     *
379
     * @return string
380
     */
381 15
    public function getContent()
382
    {
383 15
        if ($this->hasContent()) {
384 15
            return stream_get_contents($this->handle);
385
        }
386
        return null;
387
    }
388
389
    /**
390
     * Adds a header with the given $name and $value.
391
     *
392
     * Creates a new \ZBateson\MailMimeParser\Header\AbstractHeader object and
393
     * registers it as a header.
394
     *
395
     * @param string $name
396
     * @param string $value
397
     */
398 86
    public function setRawHeader($name, $value)
399
    {
400 86
        $this->headers[strtolower($name)] = $this->headerFactory->newInstance($name, $value);
401 86
    }
402
403
    /**
404
     * Removes the header with the given name
405
     *
406
     * @param string $name
407
     */
408 8
    public function removeHeader($name)
409
    {
410 8
        unset($this->headers[strtolower($name)]);
411 8
    }
412
413
    /**
414
     * Returns the AbstractHeader object for the header with the given $name
415
     *
416
     * Note that mime headers aren't case sensitive.
417
     *
418
     * @param string $name
419
     * @return \ZBateson\MailMimeParser\Header\AbstractHeader
420
     */
421 88
    public function getHeader($name)
422
    {
423 88
        if (isset($this->headers[strtolower($name)])) {
424 86
            return $this->headers[strtolower($name)];
425
        }
426 83
        return null;
427
    }
428
429
    /**
430
     * Returns the string value for the header with the given $name.
431
     *
432
     * Note that mime headers aren't case sensitive.
433
     *
434
     * @param string $name
435
     * @param string $defaultValue
436
     * @return string
437
     */
438 85
    public function getHeaderValue($name, $defaultValue = null)
439
    {
440 85
        $header = $this->getHeader($name);
441 85
        if ($header !== null) {
442 84
            return $header->getValue();
443
        }
444 82
        return $defaultValue;
445
    }
446
447
    /**
448
     * Returns the full array of headers for this part.
449
     *
450
     * @return \ZBateson\MailMimeParser\Header\AbstractHeader[]
451
     */
452 81
    public function getHeaders()
453
    {
454 81
        return $this->headers;
455
    }
456
457
    /**
458
     * Returns a parameter of the header $header, given the parameter named
459
     * $param.
460
     *
461
     * Only headers of type
462
     * \ZBateson\MailMimeParser\Header\ParameterHeader have parameters.
463
     * Content-Type and Content-Disposition are examples of headers with
464
     * parameters. "Charset" is a common parameter of Content-Type.
465
     *
466
     * @param string $header
467
     * @param string $param
468
     * @param string $defaultValue
469
     * @return string
470
     */
471 85
    public function getHeaderParameter($header, $param, $defaultValue = null)
472
    {
473 85
        $obj = $this->getHeader($header);
474 85
        if ($obj && $obj instanceof ParameterHeader) {
475 84
            return $obj->getValueFor($param, $defaultValue);
476
        }
477 7
        return $defaultValue;
478
    }
479
480
    /**
481
     * Sets the parent part.
482
     *
483
     * @param \ZBateson\MailMimeParser\Message\MimePart $part
484
     */
485 82
    public function setParent(MimePart $part)
486
    {
487 82
        $this->parent = $part;
488 82
    }
489
490
    /**
491
     * Returns this part's parent.
492
     *
493
     * @return \ZBateson\MailMimeParser\Message\MimePart
494
     */
495 84
    public function getParent()
496
    {
497 84
        return $this->parent;
498
    }
499
}
500