|
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\Header\Consumer; |
|
8
|
|
|
|
|
9
|
|
|
use ZBateson\MailMimeParser\Header\Part\Token; |
|
10
|
|
|
use ZBateson\MailMimeParser\Header\Part\AddressGroupPart; |
|
11
|
|
|
|
|
12
|
|
|
/** |
|
13
|
|
|
* Parses a single part of an address header. |
|
14
|
|
|
* |
|
15
|
|
|
* Represents a single part of a list of addresses. A part could be one email |
|
16
|
|
|
* address, or one 'group' containing multiple addresses. The consumer ends on |
|
17
|
|
|
* finding either a comma token, representing a separation between addresses, or |
|
18
|
|
|
* a semi-colon token representing the end of a group. |
|
19
|
|
|
* |
|
20
|
|
|
* A single email address may consist of just an email, or a name and an email |
|
21
|
|
|
* address. Both of these are valid examples of a From header: |
|
22
|
|
|
* - From: [email protected] |
|
23
|
|
|
* - From: Jon Snow <[email protected]> |
|
24
|
|
|
* |
|
25
|
|
|
* Groups must be named, for example: |
|
26
|
|
|
* - To: Winterfell: [email protected], Arya Stark <[email protected]>; |
|
27
|
|
|
* |
|
28
|
|
|
* Addresses may contain quoted parts and comments, and names may be mime-header |
|
29
|
|
|
* encoded (need to review RFC to be sure of this as its been a while). |
|
30
|
|
|
* |
|
31
|
|
|
* @author Zaahid Bateson |
|
32
|
|
|
*/ |
|
33
|
|
|
class AddressConsumer extends AbstractConsumer |
|
34
|
|
|
{ |
|
35
|
|
|
/** |
|
36
|
|
|
* Returns the following as sub-consumers: |
|
37
|
|
|
* - \ZBateson\MailMimeParser\Header\Consumer\AddressGroupConsumer |
|
38
|
|
|
* - \ZBateson\MailMimeParser\Header\Consumer\CommentConsumer |
|
39
|
|
|
* - \ZBateson\MailMimeParser\Header\Consumer\QuotedStringConsumer |
|
40
|
|
|
* |
|
41
|
|
|
* @return AbstractConsumer[] the sub-consumers |
|
42
|
|
|
*/ |
|
43
|
|
|
protected function getSubConsumers() |
|
44
|
|
|
{ |
|
45
|
|
|
return [ |
|
46
|
|
|
$this->consumerService->getAddressGroupConsumer(), |
|
47
|
|
|
$this->consumerService->getCommentConsumer(), |
|
48
|
|
|
$this->consumerService->getQuotedStringConsumer(), |
|
49
|
|
|
]; |
|
50
|
|
|
} |
|
51
|
|
|
|
|
52
|
|
|
/** |
|
53
|
|
|
* Overridden to return patterns matching the beginning part of an address |
|
54
|
|
|
* in a name/address part ("<" and ">" chars), end tokens ("," and ";"), and |
|
55
|
|
|
* whitespace. |
|
56
|
|
|
* |
|
57
|
|
|
* @return string[] the patterns |
|
58
|
|
|
*/ |
|
59
|
|
|
public function getTokenSeparators() |
|
60
|
|
|
{ |
|
61
|
|
|
return ['<', '>', ',', ';', '\s+']; |
|
62
|
|
|
} |
|
63
|
|
|
|
|
64
|
|
|
/** |
|
65
|
|
|
* Returns true for commas and semi-colons. |
|
66
|
|
|
* |
|
67
|
|
|
* Although the semi-colon is not strictly the end token of an |
|
68
|
|
|
* AddressConsumer, it could end a parent AddressGroupConsumer. I can't |
|
69
|
|
|
* think of a valid scenario where this would be an issue, but additional |
|
70
|
|
|
* thought may be needed (and documented here). |
|
71
|
|
|
* |
|
72
|
|
|
* @param string $token |
|
73
|
|
|
* @return boolean false |
|
74
|
|
|
*/ |
|
75
|
|
|
protected function isEndToken($token) |
|
76
|
|
|
{ |
|
77
|
|
|
return ($token === ',' || $token === ';'); |
|
78
|
|
|
} |
|
79
|
|
|
|
|
80
|
|
|
/** |
|
81
|
|
|
* AddressConsumer is "greedy", so this always returns true. |
|
82
|
|
|
* |
|
83
|
|
|
* @param string $token |
|
84
|
|
|
* @return boolean false |
|
85
|
|
|
*/ |
|
86
|
|
|
protected function isStartToken($token) |
|
87
|
|
|
{ |
|
88
|
|
|
return true; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
/** |
|
92
|
|
|
* Performs final processing on parsed parts. |
|
93
|
|
|
* |
|
94
|
|
|
* AddressConsumer's implementation looks for tokens representing the |
|
95
|
|
|
* beginning of an address part, to create a Part\AddressPart out of a |
|
96
|
|
|
* name/address pair, or assign the name part to a parsed Part\AddressGroupPart |
|
97
|
|
|
* returned from its AddressGroupConsumer sub-consumer. |
|
98
|
|
|
* |
|
99
|
|
|
* The returned array consists of a single element - either a |
|
100
|
|
|
* Part\AddressPart or a Part\AddressGroupPart. |
|
101
|
|
|
* |
|
102
|
|
|
* @param ZBateson\MailMimeParser\Header\Part\HeaderPart[] $parts |
|
103
|
|
|
* @return ZBateson\MailMimeParser\Header\Part\HeaderPart[] |
|
104
|
|
|
*/ |
|
105
|
|
|
protected function processParts(array $parts) |
|
106
|
|
|
{ |
|
107
|
|
|
$strName = ''; |
|
108
|
|
|
$strValue = ''; |
|
109
|
|
|
foreach ($parts as $part) { |
|
110
|
|
|
$p = $part->getValue(); |
|
111
|
|
|
if ($part instanceof AddressGroupPart) { |
|
112
|
|
|
return [ |
|
|
|
|
|
|
113
|
|
|
$this->partFactory->newAddressGroupPart( |
|
114
|
|
|
$part->getAddresses(), |
|
115
|
|
|
$strValue |
|
116
|
|
|
) |
|
117
|
|
|
]; |
|
118
|
|
|
} elseif ($part instanceof Token) { |
|
119
|
|
|
if ($p === '<') { |
|
120
|
|
|
$strName = $strValue; |
|
121
|
|
|
$strValue = ''; |
|
122
|
|
|
continue; |
|
123
|
|
|
} elseif ($p === '>') { |
|
124
|
|
|
continue; |
|
125
|
|
|
} |
|
126
|
|
|
} |
|
127
|
|
|
$strValue .= $part->getValue(); |
|
128
|
|
|
} |
|
129
|
|
|
return [$this->partFactory->newAddressPart($strName, $strValue)]; |
|
|
|
|
|
|
130
|
|
|
} |
|
131
|
|
|
} |
|
132
|
|
|
|
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.
Let’s take a look at an example:
Our function
my_functionexpects aPostobject, and outputs the author of the post. The base classPostreturns a simple string and outputting a simple string will work just fine. However, the child classBlogPostwhich is a sub-type ofPostinstead decided to return anobject, and is therefore violating the SOLID principles. If aBlogPostwere passed tomy_function, PHP would not complain, but ultimately fail when executing thestrtouppercall in its body.