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 html parts and complete html body #557

Merged
merged 3 commits into from
Feb 17, 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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ composer.lock
phpunit.xml
wait-for-it
coverage/

.DS_Store
14 changes: 8 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,19 @@
],
"require": {
"php": "~8.1.0 || ~8.2.0",
"ext-dom": "*",
"ext-iconv": "*",
"ext-imap": "*",
"ext-libxml": "*",
"ext-mbstring": "*"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.13.2",
"laminas/laminas-mail": "^2.21.1",
"phpstan/phpstan": "^1.9.8",
"phpstan/phpstan-phpunit": "^1.3.3",
"phpstan/phpstan-strict-rules": "^1.4.4",
"phpunit/phpunit": "^9.5.27"
"friendsofphp/php-cs-fixer": "^3.14.4",
"laminas/laminas-mail": "^2.22.0",
"phpstan/phpstan": "^1.9.17",
"phpstan/phpstan-phpunit": "^1.3.4",
"phpstan/phpstan-strict-rules": "^1.4.5",
"phpunit/phpunit": "^9.6.3"
},
"autoload": {
"psr-4": {
Expand Down
62 changes: 57 additions & 5 deletions src/Message/AbstractMessage.php
Original file line number Diff line number Diff line change
Expand Up @@ -203,23 +203,75 @@ final public function getReferences(): array
}

/**
* Get body HTML.
* Get first body HTML part.
*/
final public function getBodyHtml(): ?string
{
$iterator = new \RecursiveIteratorIterator($this, \RecursiveIteratorIterator::SELF_FIRST);
$htmlParts = $this->getBodyHtmlParts();

return $htmlParts[0] ?? null;
}

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

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

return null;
return [];
}

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

if (1 === \count($htmlParts)) {
return $htmlParts[0];
}
if (0 === \count($htmlParts)) {
return null;
}
\libxml_use_internal_errors(true); // Suppress parse errors, get errors with libxml_get_errors();

$newDom = new \DOMDocument();

$newBody = '';
$newDom->loadHTML(\implode('', $htmlParts));

$bodyTags = $newDom->getElementsByTagName('body');

foreach ($bodyTags as $body) {
foreach ($body->childNodes as $node) {
$newBody .= $newDom->saveHTML($node);
}
}

$newDom = new \DOMDocument();
$newDom->loadHTML($newBody);

$completeHtml = $newDom->saveHTML();

return false === $completeHtml ? null : $completeHtml;
}

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

/**
* Get body HTML.
* Get first body HTML part.
*
* @return null|string Null if message has no HTML message part
*/
public function getBodyHtml(): ?string;

/**
* Get all body HTML parts as array.
*
* @return string[]
*/
public function getBodyHtmlParts(): array;

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

/**
* Get body text.
*/
Expand Down
51 changes: 51 additions & 0 deletions tests/MessageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,57 @@ public function testNestesEmbeddedWithAttachment(): void
}
}

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

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

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

// Test html parts
static::assertCount(3, $message->getBodyHtmlParts());

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

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

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

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

static::assertCount(0, $message->getBodyHtmlParts());

static::assertNull($message->getCompleteBodyHtml());
}

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

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

static::assertCount(1, $message->getBodyHtmlParts());

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

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