Issues (76)

src/Header/AbstractHeader.php (3 issues)

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 Psr\Log\LoggerInterface;
11
use Psr\Log\LogLevel;
12
use ZBateson\MailMimeParser\ErrorBag;
13
use ZBateson\MailMimeParser\Header\Consumer\IConsumerService;
14
use ZBateson\MailMimeParser\Header\Part\CommentPart;
15
use ZBateson\MailMimeParser\MailMimeParser;
16
17
/**
18
 * Abstract base class representing a mime email's header.
19
 *
20
 * The base class sets up the header's consumer for parsing, sets the name of
21
 * the header, and calls the consumer to parse the header's value.
22
 *
23
 * @author Zaahid Bateson
24
 */
25
abstract class AbstractHeader extends ErrorBag implements IHeader
26
{
27
    /**
28
     * @var string the name of the header
29
     */
30
    protected string $name;
31
32
    /**
33
     * @var IHeaderPart[] all parts not including CommentParts.
34
     */
35
    protected array $parts = [];
36
37
    /**
38
     * @var IHeaderPart[] the header's parts (as returned from the consumer),
39
     *      including commentParts
40
     */
41
    protected array $allParts = [];
42
43
    /**
44
     * @var string the raw value
45
     */
46
    protected string $rawValue;
47
48
    /**
49
     * @var string[] array of comments, initialized on demand in getComments()
50
     */
51
    private ?array $comments = null;
52
53
    /**
54
     * Assigns the header's name and raw value, then calls parseHeaderValue to
55
     * extract a parsed value.
56
     *
57
     * @param IConsumerService $consumerService For parsing the value.
58
     * @param string $name Name of the header.
59
     * @param string $value Value of the header.
60
     */
61 178
    public function __construct(
62
        LoggerInterface $logger,
63
        IConsumerService $consumerService,
64
        string $name,
65
        string $value
66
    ) {
67 178
        parent::__construct($logger);
68 178
        $this->name = $name;
69 178
        $this->rawValue = $value;
70 178
        $this->parseHeaderValue($consumerService, $value);
71
    }
72
73
    /**
74
     * Filters $this->allParts into the parts required by $this->parts
75
     * and assigns it.
76
     *
77
     * The AbstractHeader::filterAndAssignToParts method filters out CommentParts.
78
     */
79 178
    protected function filterAndAssignToParts() : void
80
    {
81 178
        $this->parts = \array_values(\array_filter($this->allParts, function($p) {
0 ignored issues
show
Expected 1 space after FUNCTION keyword; 0 found
Loading history...
82 173
            return !($p instanceof CommentPart);
83 178
        }));
84
    }
85
86
    /**
87
     * Calls the consumer and assigns the parsed parts to member variables.
88
     *
89
     * The default implementation assigns the returned value to $this->allParts
90
     * and filters out comments from it, assigning the filtered array to
91
     * $this->parts by calling filterAndAssignToParts.
92
     */
93 178
    protected function parseHeaderValue(IConsumerService $consumer, string $value) : void
94
    {
95 178
        $this->allParts = $consumer($value);
96 178
        $this->filterAndAssignToParts();
97
    }
98
99
    /**
100
     * @return IHeaderPart[]
101
     */
102 11
    public function getParts() : array
103
    {
104 11
        return $this->parts;
105
    }
106
107
    /**
108
     * @return IHeaderPart[]
109
     */
110 2
    public function getAllParts() : array
111
    {
112 2
        return $this->allParts;
113
    }
114
115
    /**
116
     * @return string[]
117
     */
118 1
    public function getComments() : array
119
    {
120 1
        if ($this->comments === null) {
121 1
            $this->comments = \array_map(fn (IHeaderPart $c) => $c->getComment(), \array_merge(...\array_map(
0 ignored issues
show
The method getComment() does not exist on ZBateson\MailMimeParser\Header\IHeaderPart. Did you maybe mean getComments()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

121
            $this->comments = \array_map(fn (IHeaderPart $c) => $c->/** @scrutinizer ignore-call */ getComment(), \array_merge(...\array_map(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
122 1
                fn ($p) => ($p instanceof CommentPart) ? [$p] : $p->getComments(),
123 1
                $this->allParts
124 1
            )));
125
        }
126 1
        return $this->comments;
127
    }
128
129 141
    public function getValue() : ?string
130
    {
131 141
        if (!empty($this->parts)) {
132 139
            return $this->parts[0]->getValue();
133
        }
134 2
        return null;
135
    }
136
137 20
    public function getRawValue() : string
138
    {
139 20
        return $this->rawValue;
140
    }
141
142 29
    public function getName() : string
143
    {
144 29
        return $this->name;
145
    }
146
147 5
    public function __toString() : string
148
    {
149 5
        return "{$this->name}: {$this->rawValue}";
150
    }
151
152 2
    public function getErrorLoggingContextName() : string
153
    {
154 2
        return 'Header::' . $this->getName();
155
    }
156
157 1
    protected function getErrorBagChildren() : array
158
    {
159 1
        return $this->getAllParts();
160
    }
161
162 1
    protected function validate() : void
163
    {
164 1
        if (\strlen(\trim($this->name)) === 0) {
165 1
            $this->addError('Header doesn\'t have a name', LogLevel::ERROR);
166
        }
167 1
        if (\strlen(\trim($this->rawValue)) === 0) {
168 1
            $this->addError('Header doesn\'t have a value', LogLevel::NOTICE);
169
        }
170
    }
171
172
    /**
173
     * Checks if the passed $value parameter is null, and if so tries to parse
174
     * a header line from $nameOrLine splitting on first occurrence of a ':'
175
     * character.
176
     *
177
     * The returned array always contains two elements.  The first being the
178
     * name (or blank if a ':' char wasn't found and $value is null), and the
179
     * second being the value.
180
     *
181
     * @return string[]
182
     */
183 5
    protected static function getHeaderPartsFrom(string $nameOrLine, ?string $value = null) : array
184
    {
185 5
        $namePart = $nameOrLine;
186 5
        $valuePart = $value;
187 5
        if ($value === null) {
188
            // full header line
189 4
            $parts = \explode(':', $nameOrLine, 2);
190 4
            $namePart = (\count($parts) > 1) ? $parts[0] : '';
191 4
            $valuePart = \trim((\count($parts) > 1) ? $parts[1] : $parts[0]);
192
        }
193 5
        return [$namePart, $valuePart];
194
    }
195
196
    /**
197
     * Parses the passed parameters into an IHeader object.
198
     *
199
     * The type of returned IHeader is determined by the name of the header.
200
     * See {@see HeaderFactory::newInstance} for more details.
201
     *
202
     * The required $nameOrLine parameter may contain either the name of a
203
     * header to parse, or a full header line, e.g. From: [email protected].  If
204
     * passing a full header line, the $value parameter must be set to null (the
205
     * default).
206
     *
207
     * Note that more specific types can be called on directly.  For instance an
208
     * AddressHeader may be created by calling AddressHeader::from() which will
209
     * ignore the name of the header, and always return an AddressHeader, or by
210
     * calling `new AddressHeader('name', 'value')` directly.
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
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