Completed
Push — master ( 35f910...0c4d2e )
by Zaahid
07:25
created

HeaderContainer::getHeaderObjects()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 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;
8
9
use ArrayIterator;
10
use IteratorAggregate;
11
use ZBateson\MailMimeParser\Header\HeaderFactory;
12
13
/**
14
 * Maintains a collection of headers for a part.
15
 *
16
 * @author Zaahid Bateson
17
 */
18
class HeaderContainer 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 5
    public function __construct(HeaderFactory $headerFactory)
60
    {
61 5
        $this->headerFactory = $headerFactory;
62 5
    }
63
64
    /**
65
     * Returns the string in lower-case, and with non-alphanumeric characters
66
     * stripped out.
67
     *
68
     * @param string $header
69
     * @return string
70
     */
71 5
    private function getNormalizedHeaderName($header)
72
    {
73 5
        return preg_replace('/[^a-z0-9]/', '', strtolower($header));
74
    }
75
76
    /**
77
     * Returns true if the passed header exists in this collection.
78
     *
79
     * @param string $name
80
     * @param int $offset
81
     * @return boolean
82
     */
83 5
    public function exists($name, $offset = 0)
84
    {
85 5
        $s = $this->getNormalizedHeaderName($name);
86 5
        return isset($this->headerMap[$s][$offset]);
87
    }
88
89
    /**
90
     * Returns the AbstractHeader object for the header with the given $name and
91
     * at the optional offset (defaulting to the first header in the collection
92
     * where more than one header with the same name exists).
93
     *
94
     * Note that mime headers aren't case sensitive.
95
     *
96
     * @param string $name
97
     * @param int $offset
98
     * @return \ZBateson\MailMimeParser\Header\AbstractHeader
99
     */
100 5
    public function get($name, $offset = 0)
101
    {
102 5
        $s = $this->getNormalizedHeaderName($name);
103 5
        if (isset($this->headerMap[$s][$offset])) {
104 5
            return $this->getByIndex($this->headerMap[$s][$offset]);
105
        }
106 4
        return null;
107
    }
108
109
    /**
110
     * Returns all headers with the passed name.
111
     *
112
     * @param string $name
113
     * @return \ZBateson\MailMimeParser\Header\AbstractHeader[]
114
     */
115 2
    public function getAll($name)
116
    {
117 2
        $s = $this->getNormalizedHeaderName($name);
118 2
        $ret = [];
119 2
        if (!empty($this->headerMap[$s])) {
120 2
            foreach ($this->headerMap[$s] as $index) {
121 2
                $ret[] = $this->getByIndex($index);
122
            }
123
        }
124 2
        return $ret;
125
    }
126
127
    /**
128
     * Returns the header in the headers array at the passed 0-based integer
129
     * index.
130
     *
131
     * @param int $index
132
     * @return \ZBateson\MailMimeParser\Header\AbstractHeader
133
     */
134 5
    private function getByIndex($index)
135
    {
136 5
        if (!isset($this->headers[$index])) {
137
            return null;
138
        }
139 5
        if ($this->headerObjects[$index] === null) {
140 5
            $this->headerObjects[$index] = $this->headerFactory->newInstance(
141 5
                $this->headers[$index][0],
142 5
                $this->headers[$index][1]
143
            );
144
        }
145 5
        return $this->headerObjects[$index];
146
    }
147
148
    /**
149
     * Removes the header from the collection with the passed name.  Defaults to
150
     * removing the first instance of the header for a collection that contains
151
     * more than one with the same passed name.
152
     *
153
     * @param string $name
154
     * @param int $offset
155
     * @return boolean
156
     */
157 2
    public function remove($name, $offset = 0)
158
    {
159 2
        $s = $this->getNormalizedHeaderName($name);
160 2
        if (isset($this->headerMap[$s][$offset])) {
161 2
            $index = $this->headerMap[$s][$offset];
162 2
            array_splice($this->headerMap[$s], $offset, 1);
163 2
            unset($this->headers[$index]);
164 2
            unset($this->headerObjects[$index]);
165 2
            return true;
166
        }
167
        return false;
168
    }
169
170
    /**
171
     * Removes all headers that match the passed name.
172
     *
173
     * @param string $name
174
     * @return boolean
175
     */
176 1
    public function removeAll($name)
177
    {
178 1
        $s = $this->getNormalizedHeaderName($name);
179 1
        if (!empty($this->headerMap[$s])) {
180 1
            foreach ($this->headerMap[$s] as $i) {
181 1
                unset($this->headers[$i]);
182 1
                unset($this->headerObjects[$i]);
183
            }
184 1
            $this->headerMap[$s] = [];
185 1
            return true;
186
        }
187
        return false;
188
    }
189
190
    /**
191
     * Adds the header to the collection.
192
     *
193
     * @param string $name
194
     * @param string $value
195
     */
196 5
    public function add($name, $value)
197
    {
198 5
        $s = $this->getNormalizedHeaderName($name);
199 5
        $this->headers[$this->nextIndex] = [ $name, $value ];
200 5
        $this->headerObjects[$this->nextIndex] = null;
201 5
        if (!isset($this->headerMap[$s])) {
202 5
            $this->headerMap[$s] = [];
203
        }
204 5
        array_push($this->headerMap[$s], $this->nextIndex);
205 5
        $this->nextIndex++;
206 5
    }
207
208
    /**
209
     * If a header exists with the passed name, and at the passed offset if more
210
     * than one exists, its value is updated.
211
     *
212
     * If a header with the passed name doesn't exist at the passed offset, it
213
     * is created at the next available offset (offset is ignored when adding).
214
     *
215
     * @param string $name
216
     * @param string $value
217
     * @param int $offset
218
     */
219 3
    public function set($name, $value, $offset = 0)
220
    {
221 3
        $s = $this->getNormalizedHeaderName($name);
222 3
        if (!isset($this->headerMap[$s][$offset])) {
223 3
            $this->add($name, $value);
224 3
            return;
225
        }
226 1
        $i = $this->headerMap[$s][$offset];
227 1
        $this->headers[$i] = [ $name, $value ];
228 1
        $this->headerObjects[$i] = null;
229 1
    }
230
231
    /**
232
     * Returns an array of AbstractHeader objects representing all headers in
233
     * this collection.
234
     *
235
     * @return AbstractHeader
236
     */
237
    public function getHeaderObjects()
238
    {
239
        return array_filter(array_map([ $this, 'getByIndex' ], array_keys($this->headers)));
240
    }
241
242
    /**
243
     * Returns an array of headers in this collection.  Each returned element in
244
     * the array is an array with the first element set to the name, and the
245
     * second its value:
246
     *
247
     * [
248
     *     [ 'Header-Name', 'Header Value' ],
249
     *     [ 'Second-Header-Name', 'Second-Header-Value' ],
250
     *     // etc...
251
     * ]
252
     *
253
     * @return string[][]
254
     */
255 5
    public function getHeaders()
256
    {
257 5
        return array_values(array_filter($this->headers));
258
    }
259
260
    /**
261
     * Returns an iterator to the headers in this collection.  Each returned
262
     * element is an array with its first element set to the header's name, and
263
     * the second to its value:
264
     *
265
     * [ 'Header-Name', 'Header Value' ]
266
     *
267
     * @return ArrayIterator
268
     */
269
    public function getIterator()
270
    {
271
        return new ArrayIterator($this->getHeaders());
272
    }
273
}
274