Passed
Push — master ( 46ed75...ca2387 )
by Zaahid
03:33
created

AbstractHeader   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 201
Duplicated Lines 0 %

Test Coverage

Coverage 93.33%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 23
eloc 48
c 1
b 0
f 0
dl 0
loc 201
ccs 56
cts 60
cp 0.9333
rs 10

15 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
A filterAndAssignToParts() 0 4 1
A getValue() 0 6 2
A getErrorLoggingContextName() 0 3 1
A getComments() 0 12 2
A validate() 0 7 3
A getErrorBagChildren() 0 3 1
A getName() 0 3 1
A from() 0 9 2
A getHeaderPartsFrom() 0 11 4
A parseHeaderValue() 0 4 1
A getAllParts() 0 3 1
A getParts() 0 3 1
A __toString() 0 3 1
A getRawValue() 0 3 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
8
namespace ZBateson\MailMimeParser\Header;
9
10
use ZBateson\MailMimeParser\MailMimeParser;
11
use ZBateson\MailMimeParser\ErrorBag;
12
use ZBateson\MailMimeParser\Header\Consumer\IConsumerService;
13
use ZBateson\MailMimeParser\Header\Part\CommentPart;
14
use Psr\Log\LogLevel;
15
16
/**
17
 * Abstract base class representing a mime email's header.
18
 *
19
 * The base class sets up the header's consumer for parsing, sets the name of
20
 * the header, and calls the consumer to parse the header's value.
21
 *
22
 * @author Zaahid Bateson
23
 */
24
abstract class AbstractHeader extends ErrorBag implements IHeader
25
{
26
    /**
27
     * @var string the name of the header
28
     */
29
    protected string $name;
30
31
    /**
32
     * @var IHeaderPart[] all parts not including CommentParts.
33
     */
34
    protected array $parts = [];
35
36
    /**
37
     * @var IHeaderPart[] the header's parts (as returned from the consumer),
38
     *      including commentParts
39
     */
40
    protected array $allParts = [];
41
42
    /**
43
     * @var string[] array of comments, initialized on demand in getComments()
44
     */
45
    private ?array $comments = null;
46
47
    /**
48
     * @var string the raw value
49
     */
50
    protected string $rawValue;
51
52
    /**
53
     * Assigns the header's name and raw value, then calls parseHeaderValue to
54
     * extract a parsed value.
55
     *
56
     * @param IConsumerService $consumerService For parsing the value.
57
     * @param string $name Name of the header.
58
     * @param string $value Value of the header.
59
     */
60 174
    public function __construct(
61
        IConsumerService $consumerService,
62
        string $name,
63
        string $value
64
    ) {
65 174
        parent::__construct();
66 174
        $this->name = $name;
67 174
        $this->rawValue = $value;
68 174
        $this->parseHeaderValue($consumerService, $value);
69
    }
70
71
    /**
72
     * Filters $this->allParts into the parts required by $this->parts
73
     * and assignes it.
74
     *
75
     * The AbstractHeader::filterAndAssignToParts method filters out CommentParts.
76
     */
77 174
    protected function filterAndAssignToParts() : void
78
    {
79 174
        $this->parts = \array_values(\array_filter($this->allParts, function ($p) {
80 162
            return !($p instanceof CommentPart);
81 174
        }));
82
    }
83
84
    /**
85
     * Calls the consumer and assigns the parsed parts to member variables.
86
     *
87
     * The default implementation assigns the returned value to $this->allParts
88
     * and filters out comments from it, assigning the filtered array to
89
     * $this->parts by calling filterAndAssignToParts.
90
     */
91 174
    protected function parseHeaderValue(IConsumerService $consumer, string $value) : void
92
    {
93 174
        $this->allParts = $consumer($value);
94 174
        $this->filterAndAssignToParts();
95
    }
96
97
    /**
98
     * @return IHeaderPart[]
99
     */
100 10
    public function getParts() : array
101
    {
102 10
        return $this->parts;
103
    }
104
105
    /**
106
     * @return IHeaderPart[]
107
     */
108
    public function getAllParts() : array
109
    {
110
        return $this->allParts;
111
    }
112
113
    /**
114
     * @return string[]
115
     */
116 1
    public function getComments() : array
117
    {
118 1
        if ($this->comments === null) {
119 1
            $this->comments = \array_values(\array_map(
120 1
                function ($p) { return $p->getComment(); },
0 ignored issues
show
Coding Style introduced by
Opening brace must be the last content on the line
Loading history...
121 1
                \array_filter(
122 1
                    $this->allParts,
123 1
                    function ($p) { return ($p instanceof CommentPart); }
0 ignored issues
show
Coding Style introduced by
Opening brace must be the last content on the line
Loading history...
124 1
                )
125 1
            ));
126
        }
127 1
        return $this->comments;
128
    }
129
130 138
    public function getValue() : ?string
131
    {
132 138
        if (!empty($this->parts)) {
133 136
            return $this->parts[0]->getValue();
134
        }
135 2
        return null;
136
    }
137
138 20
    public function getRawValue() : string
139
    {
140 20
        return $this->rawValue;
141
    }
142
143 29
    public function getName() : string
144
    {
145 29
        return $this->name;
146
    }
147
148 5
    public function __toString() : string
149
    {
150 5
        return "{$this->name}: {$this->rawValue}";
151
    }
152
153 2
    public function getErrorLoggingContextName(): string
154
    {
155 2
        return 'Header::' . $this->getName();
156
    }
157
158
    protected function getErrorBagChildren() : array
159
    {
160
        return $this->getAllParts();
161
    }
162
163 1
    protected function validate() : void
164
    {
165 1
        if (strlen(trim($this->name)) === 0) {
166 1
            $this->addError('Header doesn\'t have a name', LogLevel::ERROR);
167
        }
168 1
        if (strlen(trim($this->rawValue)) === 0) {
169 1
            $this->addError('Header doesn\'t have a value', LogLevel::NOTICE);
170
        }
171
    }
172
173
    /**
174
     * Checks if the passed $value parameter is null, and if so tries to parse
175
     * a header line from $nameOrLine splitting on first occurrence of a ':'
176
     * character.
177
     *
178
     * The returned array always contains two elements.  The first being the
179
     * name (or blank if a ':' char wasn't found and $value is null), and the
180
     * second being the value.
181
     *
182
     * @return string[]
183
     */
184 5
    protected static function getHeaderPartsFrom(string $nameOrLine, ?string $value = null) : array
185
    {
186 5
        $namePart = $nameOrLine;
187 5
        $valuePart = $value;
188 5
        if ($value === null) {
189
            // full header line
190 4
            $parts = explode(':', $nameOrLine, 2);
191 4
            $namePart = (count($parts) > 1) ? $parts[0] : '';
192 4
            $valuePart = trim((count($parts) > 1) ? $parts[1] : $parts[0]);
193
        }
194 5
        return [ $namePart, $valuePart ];
195
    }
196
197
    /**
198
     * Parses the passed parameters into an IHeader object.
199
     *
200
     * The type of returned IHeader is determined by the name of the header.
201
     * See {@see HeaderFactory::newInstance} for more details.
202
     *
203
     * The required $nameOrLine parameter may contain either the name of a
204
     * header to parse, or a full header line, e.g. From: [email protected].  If
205
     * passing a full header line, the $value parameter must be set to null (the
206
     * default).
207
     *
208
     * Note that more specific types can be called on directly.  For instance an
209
     * AddressHeader may be created by calling AddressHeader::from() which will
210
     * ignore the name of the header, and always return an AddressHeader.
211
     *
212
     * @param string $nameOrLine The header's name or full header line.
213
     * @param string|null $value The header's value, or null if passing a full
214
     *        header line to parse.
215
     */
216 5
    public static function from(string $nameOrLine, ?string $value = null) : IHeader
217
    {
218 5
        $parts = static::getHeaderPartsFrom($nameOrLine, $value);
219 5
        $container = MailMimeParser::getGlobalContainer();
220 5
        $hf = $container->get(HeaderFactory::class);
221 5
        if (self::class !== static::class) {
0 ignored issues
show
introduced by
The condition self::class !== static::class is always false.
Loading history...
222 1
            return $hf->newInstanceOf($parts[0], $parts[1], static::class);
223
        }
224 4
        return $hf->newInstance($parts[0], $parts[1]);
225
    }
226
}
227