Test Failed
Pull Request — master (#171)
by Zaahid
04:55
created

PartHeaderContainer::getIterator()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
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\Message;
8
9
use ZBateson\MailMimeParser\Header\HeaderFactory;
10
use ArrayIterator;
11
use IteratorAggregate;
12
13
/**
14
 * Maintains a collection of headers for a part.
15
 *
16
 * @author Zaahid Bateson
17
 */
18
class PartHeaderContainer implements IteratorAggregate
19
{
20
    /**
21
     * @var HeaderFactory the HeaderFactory object used for created headers
22
     */
23
    protected $headerFactory;
24
25
    /**
26
     * @var string[][] Each element in the array is an array with its first
27
     * element set to the header's name, and the second its value.
28
     */
29
    private $headers = [];
30
31
    /**
32
     * @var \ZBateson\MailMimeParser\Header\AbstractHeader[] Each element is an
33
     *      AbstractHeader representing the header at the same index in the
34
     *      $headers array.  If an AbstractHeader has not been constructed for
35
     *      the header at that index, the element would be set to null.
36
     */
37
    private $headerObjects = [];
38
39
    /**
40
     * @var array Maps header names by their "normalized" (lower-cased,
41
     *      non-alphanumeric characters stripped) name to an array of indexes in
42
     *      the $headers array.  For example:
43
     *      $headerMap['contenttype] = [ 1, 4 ]
44
     *      would indicate that the headers in $headers[1] and $headers[4] are
45
     *      both headers with the name 'Content-Type' or 'contENTtype'.
46
     */
47
    private $headerMap = [];
48
49
    /**
50
     * @var int the next index to use for $headers and $headerObjects.
51
     */
52
    private $nextIndex = 0;
53
54
    /**
55
     * Constructor
56
     *
57
     * @param HeaderFactory $headerFactory
58
     */
59
    public function __construct(HeaderFactory $headerFactory, PartHeaderContainer $clone = null)
60
    {
61
        $this->headerFactory = $headerFactory;
62
        if ($clone !== null) {
63
            $this->headers = $clone->headers;
64
            $this->headerObjects = $clone->headerObjects;
65
            $this->headerMap = $clone->headerMap;
66
            $this->nextIndex = $clone->nextIndex;
67
        }
68
    }
69
70
    /**
71
     * Returns true if the passed header exists in this collection.
72
     *
73
     * @param string $name
74
     * @param int $offset
75
     * @return boolean
76
     */
77
    public function exists($name, $offset = 0)
78
    {
79
        $s = $this->headerFactory->getNormalizedHeaderName($name);
80
        return isset($this->headerMap[$s][$offset]);
81
    }
82
83
    /**
84
     * Returns an array of header indexes with names that more closely match
85
     * the passed $name if available: for instance if there are two headers in
86
     * an email, "Content-Type" and "ContentType", and the query is for a header
87
     * with the name "Content-Type", only headers that match exactly
88
     * "Content-Type" would be returned.
89
     *
90
     * @param string $name
91
     * @return int[]
92
     */
93
    private function getAllWithOriginalHeaderNameIfSet($name)
94
    {
95
        $s = $this->headerFactory->getNormalizedHeaderName($name);
96
        if (isset($this->headerMap[$s])) {
97
            $self = $this;
98
            $filtered = array_filter($this->headerMap[$s], function ($h) use ($name, $self) {
99
                return (strcasecmp($self->headers[$h][0], $name) === 0);
100
            });
101
            return (!empty($filtered)) ? $filtered : $this->headerMap[$s];
102
        }
103
        return null;
104
    }
105
106
    /**
107
     * Returns the AbstractHeader object for the header with the given $name and
108
     * at the optional offset (defaulting to the first header in the collection
109
     * where more than one header with the same name exists).
110
     *
111
     * Note that mime headers aren't case sensitive.
112
     *
113
     * @param string $name
114
     * @param int $offset
115
     * @return \ZBateson\MailMimeParser\Header\AbstractHeader
116
     */
117
    public function get($name, $offset = 0)
118
    {
119
        $a = $this->getAllWithOriginalHeaderNameIfSet($name);
120
        if (!empty($a) && isset($a[$offset])) {
121
            return $this->getByIndex($a[$offset]);
122
        }
123
        return null;
124
    }
125
126
    /**
127
     * Returns all headers with the passed name.
128
     *
129
     * @param string $name
130
     * @return \ZBateson\MailMimeParser\Header\AbstractHeader[]
131
     */
132
    public function getAll($name)
133
    {
134
        $a = $this->getAllWithOriginalHeaderNameIfSet($name);
135
        if (!empty($a)) {
136
            $self = $this;
137
            return array_map(function ($index) use ($self) {
138
                return $self->getByIndex($index);
139
            }, $a);
140
        }
141
        return [];
142
    }
143
144
    /**
145
     * Returns the header in the headers array at the passed 0-based integer
146
     * index.
147
     *
148
     * @param int $index
149
     * @return \ZBateson\MailMimeParser\Header\AbstractHeader
150
     */
151
    private function getByIndex($index)
152
    {
153
        if (!isset($this->headers[$index])) {
154
            return null;
155
        }
156
        if ($this->headerObjects[$index] === null) {
157
            $this->headerObjects[$index] = $this->headerFactory->newInstance(
158
                $this->headers[$index][0],
159
                $this->headers[$index][1]
160
            );
161
        }
162
        return $this->headerObjects[$index];
163
    }
164
165
    /**
166
     * Removes the header from the collection with the passed name.  Defaults to
167
     * removing the first instance of the header for a collection that contains
168
     * more than one with the same passed name.
169
     *
170
     * @param string $name
171
     * @param int $offset
172
     * @return boolean
173
     */
174
    public function remove($name, $offset = 0)
175
    {
176
        $s = $this->headerFactory->getNormalizedHeaderName($name);
177
        if (isset($this->headerMap[$s][$offset])) {
178
            $index = $this->headerMap[$s][$offset];
179
            array_splice($this->headerMap[$s], $offset, 1);
180
            unset($this->headers[$index]);
181
            unset($this->headerObjects[$index]);
182
            return true;
183
        }
184
        return false;
185
    }
186
187
    /**
188
     * Removes all headers that match the passed name.
189
     *
190
     * @param string $name
191
     * @return boolean
192
     */
193
    public function removeAll($name)
194
    {
195
        $s = $this->headerFactory->getNormalizedHeaderName($name);
196
        if (!empty($this->headerMap[$s])) {
197
            foreach ($this->headerMap[$s] as $i) {
198
                unset($this->headers[$i]);
199
                unset($this->headerObjects[$i]);
200
            }
201
            $this->headerMap[$s] = [];
202
            return true;
203
        }
204
        return false;
205
    }
206
207
    /**
208
     * Adds the header to the collection.
209
     *
210
     * @param string $name
211
     * @param string $value
212
     */
213
    public function add($name, $value)
214
    {
215
        $s = $this->headerFactory->getNormalizedHeaderName($name);
216
        $this->headers[$this->nextIndex] = [ $name, $value ];
217
        $this->headerObjects[$this->nextIndex] = null;
218
        if (!isset($this->headerMap[$s])) {
219
            $this->headerMap[$s] = [];
220
        }
221
        array_push($this->headerMap[$s], $this->nextIndex);
222
        $this->nextIndex++;
223
    }
224
225
    /**
226
     * If a header exists with the passed name, and at the passed offset if more
227
     * than one exists, its value is updated.
228
     *
229
     * If a header with the passed name doesn't exist at the passed offset, it
230
     * is created at the next available offset (offset is ignored when adding).
231
     *
232
     * @param string $name
233
     * @param string $value
234
     * @param int $offset
235
     */
236
    public function set($name, $value, $offset = 0)
237
    {
238
        $s = $this->headerFactory->getNormalizedHeaderName($name);
239
        if (!isset($this->headerMap[$s][$offset])) {
240
            $this->add($name, $value);
241
            return;
242
        }
243
        $i = $this->headerMap[$s][$offset];
244
        $this->headers[$i] = [ $name, $value ];
245
        $this->headerObjects[$i] = null;
246
    }
247
248
    /**
249
     * Returns an array of AbstractHeader objects representing all headers in
250
     * this collection.
251
     *
252
     * @return AbstractHeader
0 ignored issues
show
Bug introduced by
The type ZBateson\MailMimeParser\Message\AbstractHeader was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
253
     */
254
    public function getHeaderObjects()
255
    {
256
        return array_filter(array_map([ $this, 'getByIndex' ], array_keys($this->headers)));
257
    }
258
259
    /**
260
     * Returns an array of headers in this collection.  Each returned element in
261
     * the array is an array with the first element set to the name, and the
262
     * second its value:
263
     *
264
     * [
265
     *     [ 'Header-Name', 'Header Value' ],
266
     *     [ 'Second-Header-Name', 'Second-Header-Value' ],
267
     *     // etc...
268
     * ]
269
     *
270
     * @return string[][]
271
     */
272
    public function getHeaders()
273
    {
274
        return array_values(array_filter($this->headers));
275
    }
276
277
    /**
278
     * Returns an iterator to the headers in this collection.  Each returned
279
     * element is an array with its first element set to the header's name, and
280
     * the second to its value:
281
     *
282
     * [ 'Header-Name', 'Header Value' ]
283
     *
284
     * @return ArrayIterator
285
     */
286
    public function getIterator()
287
    {
288
        return new ArrayIterator($this->getHeaders());
289
    }
290
}
291