Completed
Push — master ( d4566f...31a5e9 )
by Zaahid
02:27
created

AddressConsumer::getSubConsumers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 8
rs 9.4285
cc 1
eloc 5
nc 1
nop 0
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 [
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array($this->part...dresses(), $strValue)); (ZBateson\MailMimeParser\...Part\AddressGroupPart[]) is incompatible with the return type of the parent method ZBateson\MailMimeParser\...tConsumer::processParts of type ZBateson\MailMimeParser\...eader\Part\HeaderPart[].

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:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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)];
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array($this->part...($strName, $strValue)); (ZBateson\MailMimeParser\Header\Part\AddressPart[]) is incompatible with the return type of the parent method ZBateson\MailMimeParser\...tConsumer::processParts of type ZBateson\MailMimeParser\...eader\Part\HeaderPart[].

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:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
130
    }
131
}
132