Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Method to get all text parts and complete text body #560

Merged
merged 6 commits into from
May 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,23 @@ Get message headers as a [\Ddeboer\Imap\Message\Headers](/src/Message/Headers.ph
$message->getHeaders();
```

Get message body as HTML or plain text:
Get message body as HTML or plain text (only first part):

```php
$message->getBodyHtml(); // Content of text/html part, if present
$message->getBodyText(); // Content of text/plain part, if present
```


Get complete body (all parts):

```php
$body = $message->getCompleteBodyHtml(); // Content of text/html part, if present
if ($body === null) { // If body is null, there are no HTML parts, so let's try getting the text body
$body = $message->getCompleteBodyText(); // Content of text/plain part, if present
}
```

Reading the message body keeps the message as unseen.
If you want to mark the message as seen:

Expand Down
10 changes: 0 additions & 10 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,6 @@ parameters:
count: 1
path: src/Mailbox.php

-
message: "#^Cannot call method getDecodedContent\\(\\) on mixed\\.$#"
count: 2
path: src/Message/AbstractMessage.php

-
message: "#^Cannot call method getSubtype\\(\\) on mixed\\.$#"
count: 2
path: src/Message/AbstractMessage.php

-
message: "#^Call to function base64_decode\\(\\) requires parameter \\#2 to be true\\.$#"
count: 1
Expand Down
62 changes: 41 additions & 21 deletions src/Message/AbstractMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -207,43 +207,54 @@ final public function getReferences(): array
*/
final public function getBodyHtml(): ?string
{
$htmlParts = $this->getBodyHtmlParts();
$htmlParts = $this->getAllContentsBySubtype(self::SUBTYPE_HTML);

return $htmlParts[0] ?? null;
}

/**
* Get body HTML parts.
* Get all contents parts of specific subtype (self::SUBTYPE_HTML or self::SUBTYPE_PLAIN).
*
* @return string[]
*/
final public function getBodyHtmlParts(): array
final public function getAllContentsBySubtype(string $subtype): array
{
$iterator = new \RecursiveIteratorIterator($this, \RecursiveIteratorIterator::SELF_FIRST);
$htmlParts = [];
$parts = [];
/** @var PartInterface $part */
foreach ($iterator as $part) {
if (self::SUBTYPE_HTML === $part->getSubtype()) {
$htmlParts[] = $part->getDecodedContent();
if ($subtype === $part->getSubtype()) {
$parts[] = $part->getDecodedContent();
}
}
if (\count($htmlParts) > 0) {
return $htmlParts;
if (\count($parts) > 0) {
return $parts;
}

// If message has no parts and is HTML, return content of message itself.
if (self::SUBTYPE_HTML === $this->getSubtype()) {
// If message has no parts and is of right type, return content of message.
if ($subtype === $this->getSubtype()) {
return [$this->getDecodedContent()];
}

return [];
}

/**
* Get body HTML parts.
*
* @return string[]
*/
final public function getBodyHtmlParts(): array
{
return $this->getAllContentsBySubtype(self::SUBTYPE_HTML);
}

/**
* Get all body HTML parts merged into 1 html.
*/
final public function getCompleteBodyHtml(): ?string
{
$htmlParts = $this->getBodyHtmlParts();
$htmlParts = $this->getAllContentsBySubtype(self::SUBTYPE_HTML);

if (1 === \count($htmlParts)) {
return $htmlParts[0];
Expand Down Expand Up @@ -279,19 +290,28 @@ final public function getCompleteBodyHtml(): ?string
*/
final public function getBodyText(): ?string
{
$iterator = new \RecursiveIteratorIterator($this, \RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $part) {
if (self::SUBTYPE_PLAIN === $part->getSubtype()) {
return $part->getDecodedContent();
}
}
$plainParts = $this->getAllContentsBySubtype(self::SUBTYPE_PLAIN);

return $plainParts[0] ?? null;
}

// If message has no parts, return content of message itself.
if (self::SUBTYPE_PLAIN === $this->getSubtype()) {
return $this->getDecodedContent();
/**
* Get all body PLAIN parts merged into 1 string.
*
* @return null|string Null if message has no PLAIN message parts
*/
final public function getCompleteBodyText(): ?string
{
$plainParts = $this->getAllContentsBySubtype(self::SUBTYPE_PLAIN);

if (1 === \count($plainParts)) {
return $plainParts[0];
}
if (0 === \count($plainParts)) {
return null;
}

return null;
return \implode("\n", $plainParts);
}

/**
Expand Down
16 changes: 15 additions & 1 deletion src/Message/BasicMessageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ public function getInReplyTo(): array;
*/
public function getReferences(): array;

/**
* Get message parts by type.
*
* @return string[]
*/
public function getAllContentsBySubtype(string $subtype): array;

/**
* Get first body HTML part.
*
Expand All @@ -132,7 +139,7 @@ public function getBodyHtmlParts(): array;
/**
* Get all body HTML parts merged into 1 html.
*
* @return null|string Null if message has no HTML message part
* @return null|string Null if message has no HTML message parts
*/
public function getCompleteBodyHtml(): ?string;

Expand All @@ -141,6 +148,13 @@ public function getCompleteBodyHtml(): ?string;
*/
public function getBodyText(): ?string;

/**
* Get all body PLAIN parts merged into 1 string.
*
* @return null|string Null if message has no PLAIN message parts
*/
public function getCompleteBodyText(): ?string;

/**
* Get attachments (if any) linked to this e-mail.
*
Expand Down
56 changes: 54 additions & 2 deletions tests/MessageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1049,6 +1049,7 @@ public function testMultipleHtmlParts(): void

// Test html parts
self::assertCount(3, $message->getBodyHtmlParts());
self::assertCount(3, $message->getAllContentsBySubtype(Message::SUBTYPE_HTML));

// Test html parts
$completeBody = $message->getCompleteBodyHtml();
Expand All @@ -1065,7 +1066,7 @@ public function testBodyHtmlEmpty(): void

$message = $this->mailbox->getMessage(1);

self::assertCount(0, $message->getBodyHtmlParts());
self::assertCount(0, $message->getAllContentsBySubtype(Message::SUBTYPE_HTML));

self::assertNull($message->getCompleteBodyHtml());
}
Expand All @@ -1076,11 +1077,62 @@ public function testBodyHtmlOnePart(): void

$message = $this->mailbox->getMessage(1);

self::assertCount(1, $message->getBodyHtmlParts());
self::assertCount(1, $message->getAllContentsBySubtype(Message::SUBTYPE_HTML));

self::assertNotNull($message->getCompleteBodyHtml());
}

public function testMultipleTextParts(): void
{
$this->mailbox->addMessage($this->getFixture('multiple_plain_parts_and_attachments'));

$message = $this->mailbox->getMessage(1);

// Test attachments
$expectedFileNames = [
'attachment1.pdf',
'attachment2.pdf',
];
$attachments = $message->getAttachments();
self::assertCount(2, $attachments);
foreach ($attachments as $attachment) {
self::assertContains($attachment->getFilename(), $expectedFileNames);
}

// Test html parts
self::assertCount(3, $message->getAllContentsBySubtype(Message::SUBTYPE_PLAIN));

// Test html parts
$completeBody = $message->getCompleteBodyText();
$completeBody = null === $completeBody ? '' : $completeBody;

self::assertStringContainsString('first', $completeBody);
self::assertStringContainsString('second', $completeBody);
self::assertStringContainsString('last', $completeBody);
}

public function testBodyTextEmpty(): void
{
$this->mailbox->addMessage($this->getFixture('html_only'));

$message = $this->mailbox->getMessage(1);

self::assertCount(0, $message->getAllContentsBySubtype(Message::SUBTYPE_PLAIN));

self::assertNull($message->getCompleteBodyText());
}

public function testBodyTextOnePart(): void
{
$this->mailbox->addMessage($this->getFixture('plain_only'));

$message = $this->mailbox->getMessage(1);

self::assertCount(1, $message->getAllContentsBySubtype(Message::SUBTYPE_PLAIN));

self::assertNotNull($message->getCompleteBodyText());
}

public function testImapMimeHeaderDecodeReturnsFalse(): void
{
$this->mailbox->addMessage($this->getFixture('imap_mime_header_decode_returns_false'));
Expand Down
Loading