Skip to content

Commit

Permalink
Separate 'email' into its own consumer #165
Browse files Browse the repository at this point in the history
  • Loading branch information
zbateson committed Nov 9, 2021
1 parent 1f20940 commit 5642025
Show file tree
Hide file tree
Showing 6 changed files with 219 additions and 7 deletions.
19 changes: 12 additions & 7 deletions src/Header/Consumer/AddressConsumer.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use ZBateson\MailMimeParser\Header\IHeaderPart;
use ZBateson\MailMimeParser\Header\Part\Token;
use ZBateson\MailMimeParser\Header\Part\AddressGroupPart;
use ZBateson\MailMimeParser\Header\Part\AddressPart;

/**
* Parses a single part of an address header.
Expand Down Expand Up @@ -45,21 +46,21 @@ protected function getSubConsumers()
{
return [
$this->consumerService->getAddressGroupConsumer(),
$this->consumerService->getAddressEmailConsumer(),
$this->consumerService->getCommentConsumer(),
$this->consumerService->getQuotedStringConsumer(),
];
}

/**
* Overridden to return patterns matching the beginning part of an address
* in a name/address part ("<" and ">" chars), end tokens ("," and ";"), and
* Overridden to return patterns matching end tokens ("," and ";"), and
* whitespace.
*
* @return string[] the patterns
*/
public function getTokenSeparators()
{
return [ '<', '>', ',', ';', '\s+' ];
return [ ',', ';', '\s+' ];
}

/**
Expand Down Expand Up @@ -129,18 +130,22 @@ private function processSinglePart(IHeaderPart $part, &$strName, &$strValue)
protected function processParts(array $parts)
{
$strName = '';
$strValue = '';
$strEmail = '';
foreach ($parts as $part) {
if ($part instanceof AddressGroupPart) {
return [
$this->partFactory->newAddressGroupPart(
$part->getAddresses(),
$strValue
$strEmail
)
];
} elseif ($part instanceof AddressPart) {
$strName = $strEmail;
$strEmail = $part->getEmail();
break;
}
$this->processSinglePart($part, $strName, $strValue);
$strEmail .= $part->getValue();
}
return [ $this->partFactory->newAddressPart($strName, $strValue) ];
return [ $this->partFactory->newAddressPart($strName, $strEmail) ];
}
}
85 changes: 85 additions & 0 deletions src/Header/Consumer/AddressEmailConsumer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?php
/**
* This file is part of the ZBateson\MailMimeParser project.
*
* @license http://opensource.org/licenses/bsd-license.php BSD
*/
namespace ZBateson\MailMimeParser\Header\Consumer;

/**
* Parses the Address portion of an email address header, for an address part
* that contains both a name and an email address, e.g. "name" <email@tld.com>.
*
* The address portion found within the '<' and '>' chars may contain comments
* and quoted portions.
*
* @author Zaahid Bateson
*/
class AddressEmailConsumer extends AbstractConsumer
{
/**
* Returns the following as sub-consumers:
* - {@see AddressGroupConsumer}
* - {@see CommentConsumer}
* - {@see QuotedStringConsumer}
*
* @return AbstractConsumer[] the sub-consumers
*/
protected function getSubConsumers()
{
return [
$this->consumerService->getCommentConsumer(),
$this->consumerService->getQuotedStringConsumer(),
];
}

/**
* Overridden to return patterns matching the beginning/end part of an
* address in a name/address part ("<" and ">" chars).
*
* @return string[] the patterns
*/
public function getTokenSeparators()
{
return [ '<', '>' ];
}

/**
* Returns true for the '>' char.
*
* @param string $token
* @return boolean false
*/
protected function isEndToken($token)
{
return ($token === '>');
}

/**
* Returns true for the '<' char.
*
* @param string $token
* @return boolean false
*/
protected function isStartToken($token)
{
return ($token === '<');
}

/**
* Returns a single AddressPart with its 'email' portion set, so an
* AddressConsumer can identify it and create an AddressPart with both a
* name and email set.
*
* @param \ZBateson\MailMimeParser\Header\IHeaderPart[] $parts
* @return \ZBateson\MailMimeParser\Header\IHeaderPart[]|array
*/
protected function processParts(array $parts)
{
$strEmail = '';
foreach ($parts as $p) {
$strEmail .= $p->getValue();
}
return [ $this->partFactory->newAddressPart('', $strEmail) ];
}
}
10 changes: 10 additions & 0 deletions src/Header/Consumer/ConsumerService.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ public function getAddressGroupConsumer()
return AddressGroupConsumer::getInstance($this, $this->partFactory);
}

/**
* Returns the AddressEmailConsumer singleton instance.
*
* @return AddressEmailConsumer
*/
public function getAddressEmailConsumer()
{
return AddressEmailConsumer::getInstance($this, $this->partFactory);
}

/**
* Returns the CommentConsumer singleton instance.
*
Expand Down
14 changes: 14 additions & 0 deletions tests/MailMimeParser/Header/Consumer/AddressBaseConsumerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,20 @@ public function testConsumeAddresses()
$this->assertEquals('Brute', $ret[2]->getName());
$this->assertEquals('brute@isThatHisName.com', $ret[2]->getEmail());
}

public function testConsumeNamesAndAddressesWithFunnyChars()
{
$emails = '"Popeye the Sailor" <Popeye@TheSailorMan.com>, "Olive" <Olive@Oil.com:>, Brute <brute@isThatHisName.com,>, NotCute <notcute@address.com;>';
$ret = $this->addressBaseConsumer->__invoke($emails);
$this->assertNotEmpty($ret);
$this->assertCount(4, $ret);

$this->assertEquals('Popeye@TheSailorMan.com', $ret[0]->getEmail());
$this->assertEquals('Olive@Oil.com:', $ret[1]->getEmail());
$this->assertEquals('Brute', $ret[2]->getName());
$this->assertEquals('brute@isThatHisName.com,', $ret[2]->getEmail());
$this->assertEquals('notcute@address.com;', $ret[3]->getEmail());
}

public function testConsumeAddressAndGroup()
{
Expand Down
91 changes: 91 additions & 0 deletions tests/MailMimeParser/Header/Consumer/AddressEmailConsumerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php
namespace ZBateson\MailMimeParser\Header\Consumer;

use LegacyPHPUnit\TestCase;

/**
* Description of AddressEmailConsumerTest
*
* @group Consumers
* @group AddressEmailConsumer
* @covers ZBateson\MailMimeParser\Header\Consumer\AddressEmailConsumer
* @covers ZBateson\MailMimeParser\Header\Consumer\AbstractConsumer
* @author Zaahid Bateson
*/
class AddressEmailConsumerTest extends TestCase
{
private $addressConsumer;

protected function legacySetUp()
{
$charsetConverter = $this->getMockBuilder('ZBateson\MbWrapper\MbWrapper')
->setMethods(['__toString'])
->getMock();
$pf = $this->getMockBuilder('ZBateson\MailMimeParser\Header\Part\HeaderPartFactory')
->setConstructorArgs([$charsetConverter])
->setMethods(['__toString'])
->getMock();
$mlpf = $this->getMockBuilder('ZBateson\MailMimeParser\Header\Part\MimeLiteralPartFactory')
->setConstructorArgs([$charsetConverter])
->setMethods(['__toString'])
->getMock();
$cs = $this->getMockBuilder('ZBateson\MailMimeParser\Header\Consumer\ConsumerService')
->setConstructorArgs([$pf, $mlpf])
->setMethods(['__toString'])
->getMock();
$this->addressConsumer = new AddressEmailConsumer($cs, $pf);
}

public function testConsumeEmail()
{
$email = 'Max.Payne@AddressUnknown.com';
$ret = $this->addressConsumer->__invoke($email);
$this->assertNotEmpty($ret);
$this->assertCount(1, $ret);

$address = $ret[0];
$this->assertInstanceOf('\ZBateson\MailMimeParser\Header\Part\AddressPart', $address);
$this->assertEquals('', $address->getName());
$this->assertEquals($email, $address->getEmail());
}

public function testConsumeEmailWithComments()
{
// can't remember any longer if this is how it should be handled
// need to review RFC
$email = 'Max(imum).Payne (comment)@AddressUnknown.com';
$ret = $this->addressConsumer->__invoke($email);
$this->assertNotEmpty($ret);
$this->assertCount(1, $ret);

$address = $ret[0];
$this->assertInstanceOf('\ZBateson\MailMimeParser\Header\Part\AddressPart', $address);
$this->assertEquals('Max.Payne@AddressUnknown.com', $address->getEmail());
}

public function testConsumeEmailWithQuotes()
{
// can't remember any longer if this is how it should be handled
// need to review RFC
$email = 'Max"(imum).Payne (comment)"@AddressUnknown.com';
$ret = $this->addressConsumer->__invoke($email);
$this->assertNotEmpty($ret);
$this->assertCount(1, $ret);

$address = $ret[0];
$this->assertInstanceOf('\ZBateson\MailMimeParser\Header\Part\AddressPart', $address);
$this->assertEquals('Max(imum).Payne(comment)@AddressUnknown.com', $address->getEmail());
}

public function testNotConsumeAddressGroup()
{
$email = 'Senate: Caesar@Dictator.com,Cicero@Philosophy.com, Marc Antony <MarcAntony@imawesome.it>';
$ret = $this->addressConsumer->__invoke($email);
$this->assertNotEmpty($ret);
$this->assertCount(1, $ret);

$address = $ret[0];
$this->assertInstanceOf('\ZBateson\MailMimeParser\Header\Part\AddressPart', $address);
$this->assertEquals('Senate:Caesar@Dictator.com,Cicero@Philosophy.com,MarcAntony<MarcAntony@imawesome.it', $address->getEmail());
}
}
7 changes: 7 additions & 0 deletions tests/MailMimeParser/Header/Consumer/ConsumerServiceTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ public function testGetAddressConsumer()
$this->assertNotNull($consumer);
$this->assertInstanceOf('\ZBateson\MailMimeParser\Header\Consumer\AddressConsumer', $consumer);
}

public function testGetAddressEmailConsumer()
{
$consumer = $this->consumerService->getAddressEmailConsumer();
$this->assertNotNull($consumer);
$this->assertInstanceOf('\ZBateson\MailMimeParser\Header\Consumer\AddressEmailConsumer', $consumer);
}

public function testGetAddressGroupConsumer()
{
Expand Down

0 comments on commit 5642025

Please sign in to comment.