Passed
Branch php8-testing (0e47ea)
by Zaahid
03:09
created

Message::getHtmlPart()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 5
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 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
namespace ZBateson\MailMimeParser;
8
9
use GuzzleHttp\Psr7;
10
use Psr\Http\Message\StreamInterface;
11
use ZBateson\MailMimeParser\Message\Helper\MessageHelperService;
12
use ZBateson\MailMimeParser\Message\Part\MimePart;
13
use ZBateson\MailMimeParser\Message\Part\MessagePart;
14
use ZBateson\MailMimeParser\Message\Part\PartBuilder;
15
use ZBateson\MailMimeParser\Message\Part\PartStreamFilterManager;
16
use ZBateson\MailMimeParser\Message\PartFilter;
17
use ZBateson\MailMimeParser\Message\PartFilterFactory;
18
use ZBateson\MailMimeParser\Stream\StreamFactory;
19
20
/**
21
 * A parsed mime message with optional mime parts depending on its type.
22
 *
23
 * A mime message may have any number of mime parts, and each part may have any
24
 * number of sub-parts, etc...
25
 *
26
 * @author Zaahid Bateson
27
 */
28
class Message extends MimePart
29
{
30
    /**
31
     * @var MessageHelperService helper class with various message manipulation
32
     *      routines.
33
     */
34
    protected $messageHelperService;
35
36
    /**
37
     * Constructor
38
     *
39
     * @param PartStreamFilterManager $partStreamFilterManager
40
     * @param StreamFactory $streamFactory
41
     * @param PartFilterFactory $partFilterFactory
42
     * @param PartBuilder $partBuilder
43
     * @param MessageHelperService $messageHelperService
44
     * @param StreamInterface $stream
45
     * @param StreamInterface $contentStream
46
     */
47 116
    public function __construct(
48
        PartStreamFilterManager $partStreamFilterManager,
49
        StreamFactory $streamFactory,
50
        PartFilterFactory $partFilterFactory,
51
        PartBuilder $partBuilder,
52
        MessageHelperService $messageHelperService,
53
        StreamInterface $stream = null,
54
        StreamInterface $contentStream = null
55
    ) {
56 116
        parent::__construct(
57 116
            $partStreamFilterManager,
58 116
            $streamFactory,
59 116
            $partFilterFactory,
60 116
            $partBuilder,
61 116
            $stream,
62 116
            $contentStream
63
        );
64 116
        $this->messageHelperService = $messageHelperService;
65 116
    }
66
67
    /**
68
     * Convenience method to parse a handle or string into a Message without
69
     * requiring including MailMimeParser, instantiating it, and calling parse.
70
     *
71
     * @param resource|string $handleOrString the resource handle to the input
72
     *        stream of the mime message, or a string containing a mime message
73
     * @return Message
74
     */
75 1
    public static function from($handleOrString)
76
    {
77 1
        $mmp = new MailMimeParser();
78 1
        return $mmp->parse($handleOrString);
79
    }
80
81
    /**
82
     * Returns the text/plain part at the given index (or null if not found.)
83
     *
84
     * @param int $index
85
     * @return MessagePart
86
     */
87 72
    public function getTextPart($index = 0)
88
    {
89 72
        return $this->getPart(
90 72
            $index,
91 72
            $this->partFilterFactory->newFilterFromInlineContentType('text/plain')
92
        );
93
    }
94
95
    /**
96
     * Returns the number of text/plain parts in this message.
97
     *
98
     * @return int
99
     */
100 1
    public function getTextPartCount()
101
    {
102 1
        return $this->getPartCount(
103 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/plain')
104
        );
105
    }
106
107
    /**
108
     * Returns the text/html part at the given index (or null if not found.)
109
     *
110
     * @param int $index
111
     * @return MessagePart
112
     */
113 36
    public function getHtmlPart($index = 0)
114
    {
115 36
        return $this->getPart(
116 36
            $index,
117 36
            $this->partFilterFactory->newFilterFromInlineContentType('text/html')
118
        );
119
    }
120
121
    /**
122
     * Returns the number of text/html parts in this message.
123
     *
124
     * @return int
125
     */
126 1
    public function getHtmlPartCount()
127
    {
128 1
        return $this->getPartCount(
129 1
            $this->partFilterFactory->newFilterFromInlineContentType('text/html')
130
        );
131
    }
132
133
    /**
134
     * Returns the attachment part at the given 0-based index, or null if none
135
     * is set.
136
     *
137
     * @param int $index
138
     * @return MessagePart
139
     */
140 5
    public function getAttachmentPart($index)
141
    {
142 5
        $attachments = $this->getAllAttachmentParts();
143 5
        if (!isset($attachments[$index])) {
144 1
            return null;
145
        }
146 5
        return $attachments[$index];
147
    }
148
149
    /**
150
     * Returns all attachment parts.
151
     *
152
     * "Attachments" are any non-multipart, non-signature and any text or html
153
     * html part witha Content-Disposition set to  'attachment'.
154
     *
155
     * @return MessagePart[]
156
     */
157 60
    public function getAllAttachmentParts()
158
    {
159 60
        $parts = $this->getAllParts(
160 60
            $this->partFilterFactory->newFilterFromArray([
161 60
                'multipart' => PartFilter::FILTER_EXCLUDE
162
            ])
163
        );
164 60
        return array_values(array_filter(
165 60
            $parts,
166
            function ($part) {
167
                return !(
168 60
                    $part->isTextPart()
169 60
                    && $part->getContentDisposition() === 'inline'
170
                );
171 60
            }
172
        ));
173
    }
174
175
    /**
176
     * Returns the number of attachments available.
177
     *
178
     * @return int
179
     */
180 57
    public function getAttachmentCount()
181
    {
182 57
        return count($this->getAllAttachmentParts());
183
    }
184
185
    /**
186
     * Returns a Psr7 Stream for the 'inline' text/plain content at the passed
187
     * $index, or null if unavailable.
188
     *
189
     * @param int $index
190
     * @param string $charset
191
     * @return StreamInterface
192
     */
193 68
    public function getTextStream($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
194
    {
195 68
        $textPart = $this->getTextPart($index);
196 68
        if ($textPart !== null) {
197 68
            return $textPart->getContentStream($charset);
198
        }
199 1
        return null;
200
    }
201
202
    /**
203
     * Returns a resource handle for the 'inline' text/plain content at the
204
     * passed $index, or null if unavailable.
205
     *
206
     * Note: this method should *not* be used and has been deprecated. Instead,
207
     * use Psr7 streams with getTextStream.  Multibyte chars will not be read
208
     * correctly with getTextResourceHandle/fread.
209
     *
210
     * @param int $index
211
     * @param string $charset
212
     * @deprecated since version 1.2.1
213
     * @return resource
214
     */
215
    public function getTextResourceHandle($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
216
    {
217
        trigger_error("getTextResourceHandle is deprecated since version 1.2.1", E_USER_DEPRECATED);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal getTextResourceHandle is...ted since version 1.2.1 does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
218
        $textPart = $this->getTextPart($index);
219
        if ($textPart !== null) {
220
            return $textPart->getContentResourceHandle($charset);
0 ignored issues
show
Deprecated Code introduced by
The function ZBateson\MailMimeParser\...ContentResourceHandle() has been deprecated: since version 1.2.1 ( Ignorable by Annotation )

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

220
            return /** @scrutinizer ignore-deprecated */ $textPart->getContentResourceHandle($charset);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
221
        }
222
        return null;
223
    }
224
225
    /**
226
     * Returns the content of the inline text/plain part at the given index.
227
     *
228
     * Reads the entire stream content into a string and returns it.  Returns
229
     * null if the message doesn't have an inline text part.
230
     *
231
     * @param int $index
232
     * @param string $charset
233
     * @return string
234
     */
235 2
    public function getTextContent($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
236
    {
237 2
        $part = $this->getTextPart($index);
238 2
        if ($part !== null) {
239 2
            return $part->getContent($charset);
240
        }
241 1
        return null;
242
    }
243
244
    /**
245
     * Returns a Psr7 Stream for the 'inline' text/html content at the passed
246
     * $index, or null if unavailable.
247
     *
248
     * @param int $index
249
     * @param string $charset
250
     * @return StreamInterface
251
     */
252 30
    public function getHtmlStream($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
253
    {
254 30
        $htmlPart = $this->getHtmlPart($index);
255 30
        if ($htmlPart !== null) {
256 30
            return $htmlPart->getContentStream($charset);
257
        }
258 1
        return null;
259
    }
260
261
    /**
262
     * Returns a resource handle for the 'inline' text/html content at the
263
     * passed $index, or null if unavailable.
264
     *
265
     * Note: this method should *not* be used and has been deprecated. Instead,
266
     * use Psr7 streams with getHtmlStream.  Multibyte chars will not be read
267
     * correctly with getHtmlResourceHandle/fread.
268
     *
269
     * @param int $index
270
     * @param string $charset
271
     * @deprecated since version 1.2.1
272
     * @return resource
273
     */
274
    public function getHtmlResourceHandle($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
275
    {
276
        trigger_error("getHtmlResourceHandle is deprecated since version 1.2.1", E_USER_DEPRECATED);
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal getHtmlResourceHandle is...ted since version 1.2.1 does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
277
        $htmlPart = $this->getHtmlPart($index);
278
        if ($htmlPart !== null) {
279
            return $htmlPart->getContentResourceHandle($charset);
0 ignored issues
show
Deprecated Code introduced by
The function ZBateson\MailMimeParser\...ContentResourceHandle() has been deprecated: since version 1.2.1 ( Ignorable by Annotation )

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

279
            return /** @scrutinizer ignore-deprecated */ $htmlPart->getContentResourceHandle($charset);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
280
        }
281
        return null;
282
    }
283
284
    /**
285
     * Returns the content of the inline text/html part at the given index.
286
     *
287
     * Reads the entire stream content into a string and returns it.  Returns
288
     * null if the message doesn't have an inline html part.
289
     *
290
     * @param int $index
291
     * @param string $charset
292
     * @return string
293
     */
294 2
    public function getHtmlContent($index = 0, $charset = MailMimeParser::DEFAULT_CHARSET)
295
    {
296 2
        $part = $this->getHtmlPart($index);
297 2
        if ($part !== null) {
298 2
            return $part->getContent($charset);
299
        }
300 1
        return null;
301
    }
302
303
    /**
304
     * Returns true if either a Content-Type or Mime-Version header are defined
305
     * in this Message.
306
     *
307
     * @return bool
308
     */
309 90
    public function isMime()
310
    {
311 90
        $contentType = $this->getHeaderValue('Content-Type');
312 90
        $mimeVersion = $this->getHeaderValue('Mime-Version');
313 90
        return ($contentType !== null || $mimeVersion !== null);
314
    }
315
316
    /**
317
     * Sets the text/plain part of the message to the passed $stringOrHandle,
318
     * either creating a new part if one doesn't exist for text/plain, or
319
     * assigning the value of $stringOrHandle to an existing text/plain part.
320
     *
321
     * The optional $charset parameter is the charset for saving to.
322
     * $stringOrHandle is expected to be in UTF-8 regardless of the target
323
     * charset.
324
     *
325
     * @param string|resource|StreamInterface $resource
326
     * @param string $charset
327
     */
328 2
    public function setTextPart($resource, $charset = 'UTF-8')
329
    {
330 2
        $this->messageHelperService
331 2
            ->getMultipartHelper()
332 2
            ->setContentPartForMimeType(
333 2
                $this, 'text/plain', $resource, $charset
334
            );
335 2
    }
336
337
    /**
338
     * Sets the text/html part of the message to the passed $stringOrHandle,
339
     * either creating a new part if one doesn't exist for text/html, or
340
     * assigning the value of $stringOrHandle to an existing text/html part.
341
     *
342
     * The optional $charset parameter is the charset for saving to.
343
     * $stringOrHandle is expected to be in UTF-8 regardless of the target
344
     * charset.
345
     *
346
     * @param string|resource|StreamInterface $resource
347
     * @param string $charset
348
     */
349 5
    public function setHtmlPart($resource, $charset = 'UTF-8')
350
    {
351 5
        $this->messageHelperService
352 5
            ->getMultipartHelper()
353 5
            ->setContentPartForMimeType(
354 5
                $this, 'text/html', $resource, $charset
355
            );
356 5
    }
357
358
    /**
359
     * Removes the text/plain part of the message at the passed index if one
360
     * exists.  Returns true on success.
361
     *
362
     * @param int $index
363
     * @return bool true on success
364
     */
365 4
    public function removeTextPart($index = 0)
366
    {
367 4
        return $this->messageHelperService
368 4
            ->getMultipartHelper()
369 4
            ->removePartByMimeType(
370 4
                $this, 'text/plain', $index
371
            );
372
    }
373
374
    /**
375
     * Removes all text/plain inline parts in this message, optionally keeping
376
     * other inline parts as attachments on the main message (defaults to
377
     * keeping them).
378
     *
379
     * @param bool $keepOtherPartsAsAttachments
380
     * @return bool true on success
381
     */
382 1
    public function removeAllTextParts($keepOtherPartsAsAttachments = true)
383
    {
384 1
        return $this->messageHelperService
385 1
            ->getMultipartHelper()
386 1
            ->removeAllContentPartsByMimeType(
387 1
                $this, 'text/plain', $keepOtherPartsAsAttachments
388
            );
389
    }
390
391
    /**
392
     * Removes the html part of the message if one exists.  Returns true on
393
     * success.
394
     *
395
     * @param int $index
396
     * @return bool true on success
397
     */
398 3
    public function removeHtmlPart($index = 0)
399
    {
400 3
        return $this->messageHelperService
401 3
            ->getMultipartHelper()
402 3
            ->removePartByMimeType(
403 3
                $this, 'text/html', $index
404
            );
405
    }
406
407
    /**
408
     * Removes all text/html inline parts in this message, optionally keeping
409
     * other inline parts as attachments on the main message (defaults to
410
     * keeping them).
411
     *
412
     * @param bool $keepOtherPartsAsAttachments
413
     * @return bool true on success
414
     */
415 2
    public function removeAllHtmlParts($keepOtherPartsAsAttachments = true)
416
    {
417 2
        return $this->messageHelperService
418 2
            ->getMultipartHelper()
419 2
            ->removeAllContentPartsByMimeType(
420 2
                $this, 'text/html', $keepOtherPartsAsAttachments
421
            );
422
    }
423
424
    /**
425
     * Adds an attachment part for the passed raw data string or handle and
426
     * given parameters.
427
     *
428
     * @param string|resource|StreamInterface $resource
429
     * @param string $mimeType
430
     * @param string $filename
431
     * @param string $disposition
432
     * @param string $encoding defaults to 'base64', only applied for a mime
433
     *        email
434
     */
435 5
    public function addAttachmentPart($resource, $mimeType, $filename = null, $disposition = 'attachment', $encoding = 'base64')
436
    {
437 5
        $this->messageHelperService
438 5
            ->getMultipartHelper()
439 5
            ->createAndAddPartForAttachment($this, $resource, $mimeType, $disposition, $filename, $encoding);
440 5
    }
441
442
    /**
443
     * Adds an attachment part using the passed file.
444
     *
445
     * Essentially creates a file stream and uses it.
446
     *
447
     * @param string $filePath
448
     * @param string $mimeType
449
     * @param string $filename
450
     * @param string $disposition
451
     */
452 5
    public function addAttachmentPartFromFile($filePath, $mimeType, $filename = null, $disposition = 'attachment', $encoding = 'base64')
453
    {
454 5
        $handle = Psr7\stream_for(fopen($filePath, 'r'));
0 ignored issues
show
Bug introduced by
The function stream_for was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

454
        $handle = /** @scrutinizer ignore-call */ Psr7\stream_for(fopen($filePath, 'r'));
Loading history...
455 5
        if ($filename === null) {
456 4
            $filename = basename($filePath);
457
        }
458 5
        $this->addAttachmentPart($handle, $mimeType, $filename, $disposition, $encoding);
459 5
    }
460
461
    /**
462
     * Removes the attachment with the given index
463
     *
464
     * @param int $index
465
     */
466 3
    public function removeAttachmentPart($index)
467
    {
468 3
        $part = $this->getAttachmentPart($index);
469 3
        $this->removePart($part);
470 3
    }
471
472
    /**
473
     * Returns a stream that can be used to read the content part of a signed
474
     * message, which can be used to sign an email or verify a signature.
475
     *
476
     * The method simply returns the stream for the first child.  No
477
     * verification of whether the message is in fact a signed message is
478
     * performed.
479
     *
480
     * Note that unlike getSignedMessageAsString, getSignedMessageStream doesn't
481
     * replace new lines.
482
     *
483
     * @return StreamInterface or null if the message doesn't have any children
484
     */
485 1
    public function getSignedMessageStream()
486
    {
487
        return $this
488 1
            ->messageHelperService
489 1
            ->getPrivacyHelper()
490 1
            ->getSignedMessageStream($this);
491
    }
492
493
    /**
494
     * Returns a string containing the entire body of a signed message for
495
     * verification or calculating a signature.
496
     *
497
     * Non-CRLF new lines are replaced to always be CRLF.
498
     *
499
     * @return string or null if the message doesn't have any children
500
     */
501 14
    public function getSignedMessageAsString()
502
    {
503
        return $this
504 14
            ->messageHelperService
505 14
            ->getPrivacyHelper()
506 14
            ->getSignedMessageAsString($this);
507
    }
508
509
    /**
510
     * Returns the signature part of a multipart/signed message or null.
511
     *
512
     * The signature part is determined to always be the 2nd child of a
513
     * multipart/signed message, the first being the 'body'.
514
     *
515
     * Using the 'protocol' parameter of the Content-Type header is unreliable
516
     * in some instances (for instance a difference of x-pgp-signature versus
517
     * pgp-signature).
518
     *
519
     * @return MimePart
520
     */
521 18
    public function getSignaturePart()
522
    {
523
        return $this
524 18
            ->messageHelperService
525 18
            ->getPrivacyHelper()
526 18
            ->getSignaturePart($this);
527
    }
528
529
    /**
530
     * Turns the message into a multipart/signed message, moving the actual
531
     * message into a child part, sets the content-type of the main message to
532
     * multipart/signed and adds an empty signature part as well.
533
     *
534
     * After calling setAsMultipartSigned, call getSignedMessageAsString to
535
     * return a
536
     *
537
     * @param string $micalg The Message Integrity Check algorithm being used
538
     * @param string $protocol The mime-type of the signature body
539
     */
540 9
    public function setAsMultipartSigned($micalg, $protocol)
541
    {
542
        $this
543 9
            ->messageHelperService
544 9
            ->getPrivacyHelper()
545 9
            ->setMessageAsMultipartSigned($this, $micalg, $protocol);
546 9
    }
547
548
    /**
549
     * Sets the signature body of the message to the passed $body for a
550
     * multipart/signed message.
551
     *
552
     * @param string $body
553
     */
554 9
    public function setSignature($body)
555
    {
556 9
        $this->messageHelperService->getPrivacyHelper()
557 9
            ->setSignature($this, $body);
558 9
    }
559
}
560