Skip to content
This repository was archived by the owner on Jan 30, 2020. It is now read-only.

Commit b986636

Browse files
committed
Merge branch 'hotfix/148-address-with-parens' into develop
Forward port #148
2 parents cd86732 + a1dc7c9 commit b986636

File tree

5 files changed

+155
-39
lines changed

5 files changed

+155
-39
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ All notable changes to this project will be documented in this file, in reverse
66

77
### Added
88

9+
- [#148](https://github.com/zendframework/zend-mail/pull/148) adds the optional constructor argument `$comment` and the method `getComment()` to the class
10+
`Zend\Mail\Address`. When a comment is present, `toString()` will include it in the representation.
11+
12+
- [#148](https://github.com/zendframework/zend-mail/pull/148) adds the method `Zend\Mail\Address::fromString(string $address, $comment = null) : Address`.
13+
The method can be used to generate an instance from a string containing a `(name)?<email>` value.
14+
The `$comment` argument can be used to associate a comment with the address.
15+
916
- [#213](https://github.com/zendframework/zend-mail/pull/213) re-adds support for PHP 5.6 and 7.0; ZF policy is never
1017
to bump the major version of a PHP requirement unless the package is bumping major version.
1118

@@ -23,6 +30,9 @@ All notable changes to this project will be documented in this file, in reverse
2330

2431
### Fixed
2532

33+
- [#148](https://github.com/zendframework/zend-mail/pull/148) fixes how `Zend\Mail\Header\AbstractAddressList` parses address values, ensuring
34+
that they now retain any address comment discovered to include in the generated `Zend\Mail\Address` instances.
35+
2636
- [#147](https://github.com/zendframework/zend-mail/pull/147) fixes how address lists are parsed, expanding the functionality to allow either
2737
`,` or `;` delimiters (or both in combination).
2838

src/Address.php

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,17 +12,56 @@
1212

1313
class Address implements Address\AddressInterface
1414
{
15+
protected $comment;
1516
protected $email;
1617
protected $name;
1718

19+
/**
20+
* Create an instance from a string value.
21+
*
22+
* Parses a string representing a single address. If it is a valid format,
23+
* it then creates and returns an instance of itself using the name and
24+
* email it has parsed from the value.
25+
*
26+
* @param string $address
27+
* @param null|string $comment Comment associated with the address, if any.
28+
* @throws Exception\InvalidArgumentException
29+
* @return self
30+
*/
31+
public static function fromString($address, $comment = null)
32+
{
33+
if (! preg_match('/^((?P<name>.*)<(?P<namedEmail>[^>]+)>|(?P<email>.+))$/', $address, $matches)) {
34+
throw new Exception\InvalidArgumentException('Invalid address format');
35+
}
36+
37+
$name = null;
38+
if (isset($matches['name'])) {
39+
$name = trim($matches['name']);
40+
}
41+
if (empty($name)) {
42+
$name = null;
43+
}
44+
45+
if (isset($matches['namedEmail'])) {
46+
$email = $matches['namedEmail'];
47+
}
48+
if (isset($matches['email'])) {
49+
$email = $matches['email'];
50+
}
51+
$email = trim($email);
52+
53+
return new static($email, $name, $comment);
54+
}
55+
1856
/**
1957
* Constructor
2058
*
2159
* @param string $email
2260
* @param null|string $name
61+
* @param null|string $comment
2362
* @throws Exception\InvalidArgumentException
2463
*/
25-
public function __construct($email, $name = null)
64+
public function __construct($email, $name = null, $comment = null)
2665
{
2766
$emailAddressValidator = new EmailAddressValidator(Hostname::ALLOW_DNS | Hostname::ALLOW_LOCAL);
2867
if (! is_string($email) || empty($email)) {
@@ -51,6 +90,10 @@ public function __construct($email, $name = null)
5190
}
5291

5392
$this->email = $email;
93+
94+
if (null !== $comment) {
95+
$this->comment = $comment;
96+
}
5497
}
5598

5699
/**
@@ -73,19 +116,50 @@ public function getName()
73116
return $this->name;
74117
}
75118

119+
/**
120+
* Retrieve comment, if any
121+
*
122+
* @return null|string
123+
*/
124+
public function getComment()
125+
{
126+
return $this->comment;
127+
}
128+
76129
/**
77130
* String representation of address
78131
*
79132
* @return string
80133
*/
81134
public function toString()
82135
{
83-
$string = '<' . $this->getEmail() . '>';
84-
$name = $this->getName();
136+
$string = sprintf('<%s>', $this->getEmail());
137+
$name = $this->constructName();
85138
if (null === $name) {
86139
return $string;
87140
}
88141

89-
return $name . ' ' . $string;
142+
return sprintf('%s %s', $name, $string);
143+
}
144+
145+
/**
146+
* Constructs the name string
147+
*
148+
* If a comment is present, appends the comment (commented using parens) to
149+
* the name before returning it; otherwise, returns just the name.
150+
*
151+
* @return null|string
152+
*/
153+
private function constructName()
154+
{
155+
$name = $this->getName();
156+
$comment = $this->getComment();
157+
158+
if ($comment === null || $comment === '') {
159+
return $name;
160+
}
161+
162+
$string = sprintf('%s (%s)', $name, $comment);
163+
return trim($string);
90164
}
91165
}

src/AddressList.php

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -88,32 +88,13 @@ public function addMany(array $addresses)
8888
* - dev@zf.com
8989
*
9090
* @param string $address
91+
* @param null|string $comment Comment associated with the address, if any.
9192
* @throws Exception\InvalidArgumentException
9293
* @return AddressList
9394
*/
94-
public function addFromString($address)
95+
public function addFromString($address, $comment = null)
9596
{
96-
if (! preg_match('/^((?P<name>.*)<(?P<namedEmail>[^>]+)>|(?P<email>.+))$/', $address, $matches)) {
97-
throw new Exception\InvalidArgumentException('Invalid address format');
98-
}
99-
100-
$name = null;
101-
if (isset($matches['name'])) {
102-
$name = trim($matches['name']);
103-
}
104-
if (empty($name)) {
105-
$name = null;
106-
}
107-
108-
if (isset($matches['namedEmail'])) {
109-
$email = $matches['namedEmail'];
110-
}
111-
if (isset($matches['email'])) {
112-
$email = $matches['email'];
113-
}
114-
$email = trim($email);
115-
116-
return $this->add($email, $name);
97+
$this->add(Address::fromString($address, $comment));
11798
}
11899

119100
/**

src/Header/AbstractAddressList.php

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
namespace Zend\Mail\Header;
99

10+
use Zend\Mail\Address;
1011
use Zend\Mail\AddressList;
1112
use Zend\Mail\Headers;
1213

@@ -53,38 +54,45 @@ public static function fromString($headerLine)
5354
$values = AddressListParser::parse($fieldValue);
5455

5556
$wasEncoded = false;
56-
array_walk(
57-
$values,
58-
function (&$value) use (&$wasEncoded) {
57+
$addresses = array_map(
58+
function ($value) use (&$wasEncoded) {
5959
$decodedValue = HeaderWrap::mimeDecodeValue($value);
6060
$wasEncoded = $wasEncoded || ($decodedValue !== $value);
61+
6162
$value = trim($decodedValue);
63+
64+
$comments = self::getComments($value);
6265
$value = self::stripComments($value);
66+
6367
$value = preg_replace(
6468
[
65-
'#(?<!\\\)"(.*)(?<!\\\)"#', //quoted-text
66-
'#\\\([\x01-\x09\x0b\x0c\x0e-\x7f])#' //quoted-pair
69+
'#(?<!\\\)"(.*)(?<!\\\)"#', // quoted-text
70+
'#\\\([\x01-\x09\x0b\x0c\x0e-\x7f])#', // quoted-pair
6771
],
6872
[
6973
'\\1',
70-
'\\1'
74+
'\\1',
7175
],
7276
$value
7377
);
74-
}
78+
79+
return empty($value) ? null : Address::fromString($value, $comments);
80+
},
81+
$values
7582
);
83+
$addresses = array_filter($addresses);
84+
7685
$header = new static();
7786
if ($wasEncoded) {
7887
$header->setEncoding('UTF-8');
7988
}
8089

81-
$values = array_filter($values);
82-
8390
/** @var AddressList $addressList */
8491
$addressList = $header->getAddressList();
85-
foreach ($values as $address) {
86-
$addressList->addFromString($address);
92+
foreach ($addresses as $address) {
93+
$addressList->add($address);
8794
}
95+
8896
return $header;
8997
}
9098

@@ -194,7 +202,38 @@ public function toString()
194202
return (empty($value)) ? '' : sprintf('%s: %s', $name, $value);
195203
}
196204

197-
// Supposed to be private, protected as a workaround for PHP bug 68194
205+
/**
206+
* Retrieve comments from value, if any.
207+
*
208+
* Supposed to be private, protected as a workaround for PHP bug 68194
209+
*
210+
* @param string $value
211+
* @return string
212+
*/
213+
protected static function getComments($value)
214+
{
215+
$matches = [];
216+
preg_match_all(
217+
'/\\(
218+
(?P<comment>(
219+
\\\\.|
220+
[^\\\\)]
221+
)+)
222+
\\)/x',
223+
$value,
224+
$matches
225+
);
226+
return isset($matches['comment']) ? implode(', ', $matches['comment']) : '';
227+
}
228+
229+
/**
230+
* Strip all comments from value, if any.
231+
*
232+
* Supposed to be private, protected as a workaround for PHP bug 68194
233+
*
234+
* @param string $value
235+
* @return void
236+
*/
198237
protected static function stripComments($value)
199238
{
200239
return preg_replace(

test/AddressListTest.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
*/
2020
class AddressListTest extends TestCase
2121
{
22-
/** @var AddressList $list */
22+
/** @var AddressList */
2323
private $list;
2424

2525
public function setUp()
@@ -95,6 +95,18 @@ public function testCanAddManyAddressesAtOnce()
9595
$this->assertTrue($this->list->has('fw-announce@lists.zend.com'));
9696
}
9797

98+
public function testLosesParensInName()
99+
{
100+
$header = '"Supports (E-mail)" <support@example.org>';
101+
102+
$to = Header\To::fromString('To:' . $header);
103+
$addressList = $to->getAddressList();
104+
$address = $addressList->get('support@example.org');
105+
$this->assertEquals('Supports', $address->getName());
106+
$this->assertEquals('E-mail', $address->getComment());
107+
$this->assertEquals('support@example.org', $address->getEmail());
108+
}
109+
98110
public function testDoesNotStoreDuplicatesAndFirstWins()
99111
{
100112
$addresses = [

0 commit comments

Comments
 (0)