Completed
Push — master ( 42b3e6...c7cf7f )
by Zaahid
02:29
created

AddressConsumer::processParts()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 1
Metric Value
c 4
b 0
f 1
dl 0
loc 17
rs 9.4285
cc 3
eloc 11
nc 3
nop 1
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\HeaderPart;
10
use ZBateson\MailMimeParser\Header\Part\Token;
11
use ZBateson\MailMimeParser\Header\Part\AddressGroupPart;
12
13
/**
14
 * Parses a single part of an address header.
15
 * 
16
 * Represents a single part of a list of addresses.  A part could be one email
17
 * address, or one 'group' containing multiple addresses.  The consumer ends on
18
 * finding either a comma token, representing a separation between addresses, or
19
 * a semi-colon token representing the end of a group.
20
 * 
21
 * A single email address may consist of just an email, or a name and an email
22
 * address.  Both of these are valid examples of a From header:
23
 *  - From: [email protected]
24
 *  - From: Jon Snow <[email protected]>
25
 * 
26
 * Groups must be named, for example:
27
 *  - To: Winterfell: [email protected], Arya Stark <[email protected]>;
28
 *
29
 * Addresses may contain quoted parts and comments, and names may be mime-header
30
 * encoded (need to review RFC to be sure of this as its been a while).
31
 * 
32
 * @author Zaahid Bateson
33
 */
34
class AddressConsumer extends AbstractConsumer
35
{
36
    /**
37
     * Returns the following as sub-consumers:
38
     *  - \ZBateson\MailMimeParser\Header\Consumer\AddressGroupConsumer
39
     *  - \ZBateson\MailMimeParser\Header\Consumer\CommentConsumer
40
     *  - \ZBateson\MailMimeParser\Header\Consumer\QuotedStringConsumer
41
     * 
42
     * @return AbstractConsumer[] the sub-consumers
43
     */
44
    protected function getSubConsumers()
45
    {
46
        return [
47
            $this->consumerService->getAddressGroupConsumer(),
48
            $this->consumerService->getCommentConsumer(),
49
            $this->consumerService->getQuotedStringConsumer(),
50
        ];
51
    }
52
    
53
    /**
54
     * Overridden to return patterns matching the beginning part of an address
55
     * in a name/address part ("<" and ">" chars), end tokens ("," and ";"), and
56
     * whitespace.
57
     * 
58
     * @return string[] the patterns
59
     */
60
    public function getTokenSeparators()
61
    {
62
        return ['<', '>', ',', ';', '\s+'];
63
    }
64
    
65
    /**
66
     * Returns true for commas and semi-colons.
67
     * 
68
     * Although the semi-colon is not strictly the end token of an
69
     * AddressConsumer, it could end a parent AddressGroupConsumer. I can't
70
     * think of a valid scenario where this would be an issue, but additional
71
     * thought may be needed (and documented here).
72
     * 
73
     * @param string $token
74
     * @return boolean false
75
     */
76
    protected function isEndToken($token)
77
    {
78
        return ($token === ',' || $token === ';');
79
    }
80
    
81
    /**
82
     * AddressConsumer is "greedy", so this always returns true.
83
     * 
84
     * @param string $token
85
     * @return boolean false
86
     */
87
    protected function isStartToken($token)
88
    {
89
        return true;
90
    }
91
    
92
    /**
93
     * Checks if the passed part represents the beginning or end of an address
94
     * part (less than/greater than characters) and either appends the value of
95
     * the part to the passed $strValue, or sets up $strName
96
     * 
97
     * @param HeaderPart $part
98
     * @param string $strName
99
     * @param string $strValue
100
     */
101
    private function processSinglePart(HeaderPart $part, &$strName, &$strValue)
102
    {
103
        $pValue = $part->getValue();
104
        if ($part instanceof Token) {
105
            if ($pValue === '<') {
106
                $strName = $strValue;
107
                $strValue = '';
108
                return;
109
            } elseif ($pValue === '>') {
110
                return;
111
            }
112
        }
113
        $strValue .= $pValue;
114
    }
115
    
116
    /**
117
     * Performs final processing on parsed parts.
118
     * 
119
     * AddressConsumer's implementation looks for tokens representing the
120
     * beginning of an address part, to create a Part\AddressPart out of a
121
     * name/address pair, or assign the name part to a parsed Part\AddressGroupPart
122
     * returned from its AddressGroupConsumer sub-consumer.
123
     * 
124
     * The returned array consists of a single element - either a
125
     * Part\AddressPart or a Part\AddressGroupPart.
126
     * 
127
     * @param \ZBateson\MailMimeParser\Header\Part\HeaderPart[] $parts
128
     * @return \ZBateson\MailMimeParser\Header\Part\HeaderPart[]|array
129
     */
130
    protected function processParts(array $parts)
131
    {
132
        $strName = '';
133
        $strValue = '';
134
        foreach ($parts as $part) {
135
            if ($part instanceof AddressGroupPart) {
136
                return [
137
                    $this->partFactory->newAddressGroupPart(
138
                        $part->getAddresses(),
139
                        $strValue
140
                    )
141
                ];
142
            }
143
            $this->processSinglePart($part, $strName, $strValue);
144
        }
145
        return [$this->partFactory->newAddressPart($strName, $strValue)];
146
    }
147
}
148