Test Failed
Branch 1.0.0 (84f469)
by Zaahid
05:36
created

PartBuilder::setStreamContentStartPos()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 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
namespace ZBateson\MailMimeParser\Message\Part;
8
9
use ZBateson\MailMimeParser\Header\HeaderFactory;
10
11
/**
12
 * Used by MessageParser to keep information about a parsed message as an
13
 * intermediary before creating a Message object and its MessagePart children.
14
 *
15
 * @author Zaahid Bateson
16
 */
17
class PartBuilder
18
{
19
    /**
20
     * @var int The offset read start position for this part (beginning of
21
     * headers) in the message's stream.
22
     */
23
    private $streamPartStartPos = 0;
24
    
25
    /**
26
     * @var int The offset read end position for this part.  If the part is a
27
     * multipart mime part, the end position is after all of this parts
28
     * children.
29
     */
30
    private $streamPartEndPos = 0;
31
    
32
    /**
33
     * @var int The offset read start position in the message's stream for the
34
     * beginning of this part's content (body).
35
     */
36
    private $streamContentStartPos = 0;
37
    
38
    /**
39
     * @var int The offset read end position in the message's stream for the
40
     * end of this part's content (body).
41
     */
42
    private $streamContentEndPos = 0;
43
44
    /**
45
     * @var \ZBateson\MailMimeParser\Header\HeaderFactory used to parse a
46
     *      Content-Type header when needed.
47
     */
48
    private $headerFactory;
49
    
50
    /**
51
     * @var \ZBateson\MailMimeParser\Message\Part\MessagePartFactory the factory
52
     *      needed for creating the Message or MessagePart for the parsed part.
53
     */
54
    private $messagePartFactory;
55
    
56
    /**
57
     * @var boolean set to true once the end boundary of the currently-parsed
58
     *      part is found.
59
     */
60
    private $endBoundaryFound = false;
61
    
62
    /**
63
     * @var boolean|null|string false if not queried for in the content-type
64
     *      header of this part, null if the current part does not have a
65
     *      boundary, or the value of the boundary parameter of the content-type
66
     *      header if the part contains one.
67
     */
68
    private $mimeBoundary = false;
69
    
70
    /**
71
     * @var string[][] an array of headers on the current part.  The key index
72
     *      is set to the lower-cased, alphanumeric-only, name of the header
73
     *      (after stripping out non-alphanumeric characters, e.g. contenttype)
74
     *      and each element containing an array of 2 strings, the first being
75
     *      the original name of the header, and the second being the value.
76
     */
77
    private $headers = [];
78
    
79
    /**
80
     * @var PartBuilder[] an array of children found below this part for a mime
81
     *      email
82
     */
83
    private $children = [];
84
    
85
    /**
86
     * @var PartBuilder the parent part.
87
     */
88
    private $parent = null;
89
    
90
    /**
91
     * @var string[] key => value pairs of properties passed on to the 
92
     *      $messagePartFactory when constructing the Message and its children.
93
     */
94
    private $properties = [];
95
    
96
    /**
97
     * @var ZBateson\MailMimeParser\Header\ParameterHeader parsed content-type
98
     *      header.
99
     */
100
    private $contentType = null;
101
    
102
    /**
103
     * @var string the PartStream protocol used to create part and content
104
     *      filenames for fopen
105
     */
106
    private $streamWrapperProtocol = null;
107
    
108
    /**
109
     * Sets up class dependencies.
110
     * 
111
     * @param HeaderFactory $hf
112
     * @param \ZBateson\MailMimeParser\Message\Part\MessagePartFactory $mpf
113
     * @param string $streamWrapperProtocol
114
     */
115
    public function __construct(
116
        HeaderFactory $hf,
117
        MessagePartFactory $mpf,
118
        $streamWrapperProtocol
119
    ) {
120
        $this->headerFactory = $hf;
121
        $this->messagePartFactory = $mpf;
122
        $this->streamWrapperProtocol = $streamWrapperProtocol;
123
    }
124
    
125
    /**
126
     * Adds a header with the given $name and $value to the headers array.
127
     *
128
     * Removes non-alphanumeric characters from $name, and sets it to lower-case
129
     * to use as a key in the private headers array.  Sets the original $name
130
     * and $value as elements in the headers' array value for the calculated
131
     * key.
132
     *
133
     * @param string $name
134
     * @param string $value
135
     */
136
    public function addHeader($name, $value)
137
    {
138
        $nameKey = preg_replace('/[^a-z0-9]/', '', strtolower($name));
139
        $this->headers[$nameKey] = [$name, $value];
140
    }
141
    
142
    /**
143
     * Returns the raw headers added to this PartBuilder as an array consisting
144
     * of:
145
     * 
146
     * Keys set to the name of the header, in all lowercase, and with non-
147
     * alphanumeric characters removed (e.g. Content-Type becomes contenttype).
148
     * 
149
     * The value is an array of two elements.  The first is the original header
150
     * name (e.g. Content-Type) and the second is the raw string value of the
151
     * header, e.g. 'text/html; charset=utf8'.
152
     * 
153
     * @return array
154
     */
155
    public function getRawHeaders()
156
    {
157
        return $this->headers;
158
    }
159
    
160
    /**
161
     * Sets the specified property denoted by $name to $value.
162
     * 
163
     * @param string $name
164
     * @param mixed $value
165
     */
166
    public function setProperty($name, $value)
167
    {
168
        $this->properties[$name] = $value;
169
    }
170
    
171
    /**
172
     * Returns the value of the property with the given $name.
173
     * 
174
     * @param string $name
175
     * @return mixed
176
     */
177
    public function getProperty($name)
178
    {
179
        if (!isset($this->properties[$name])) {
180
            return null;
181
        }
182
        return $this->properties[$name];
183
    }
184
    
185
    /**
186
     * Registers the passed PartBuilder as a child of the current PartBuilder.
187
     * 
188
     * @param \ZBateson\MailMimeParser\Message\PartBuilder $partBuilder
189
     */
190
    public function addChild(PartBuilder $partBuilder)
191
    {
192
        $partBuilder->setParent($this);
193
        $this->children[] = $partBuilder;
194
    }
195
    
196
    /**
197
     * Returns all children PartBuilder objects.
198
     * 
199
     * @return \ZBateson\MailMimeParser\Message\PartBuilder[]
200
     */
201
    public function getChildren()
202
    {
203
        return $this->children;
204
    }
205
    
206
    /**
207
     * Registers the passed PartBuilder as the parent of the current
208
     * PartBuilder.
209
     * 
210
     * @param \ZBateson\MailMimeParser\Message\Part\PartBuilder $partBuilder
211
     */
212
    public function setParent(PartBuilder $partBuilder)
213
    {
214
        $this->parent = $partBuilder;
215
    }
216
    
217
    /**
218
     * Returns this PartBuilder's parent.
219
     * 
220
     * @return PartBuilder
221
     */
222
    public function getParent()
223
    {
224
        return $this->parent;
225
    }
226
    
227
    /**
228
     * Returns true if either a Content-Type or Mime-Version header are defined
229
     * in this PartBuilder's headers.
230
     * 
231
     * @return boolean
232
     */
233
    public function isMime()
234
    {
235
        return (isset($this->headers['contenttype'])
236
            || isset($this->headers['mimeversion']));
237
    }
238
    
239
    /**
240
     * Returns a ParameterHeader representing the parsed Content-Type header for
241
     * this PartBuilder.
242
     * 
243
     * @return \ZBateson\MailMimeParser\Header\ParameterHeader
244
     */
245
    public function getContentType()
246
    {
247
        if ($this->contentType === null && isset($this->headers['contenttype'])) {
248
            $this->contentType = $this->headerFactory->newInstance(
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->headerFactory->ne...ders['contenttype'][1]) of type object<ZBateson\MailMime...\Header\AbstractHeader> is incompatible with the declared type object<ZBateson\MailMime...Header\ParameterHeader> of property $contentType.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
249
                'Content-Type',
250
                $this->headers['contenttype'][1]
251
            );
252
        }
253
        return $this->contentType;
254
    }
255
    
256
    /**
257
     * Returns the parsed boundary parameter of the Content-Type header if set
258
     * for a multipart message part.
259
     * 
260
     * @return string
261
     */
262
    public function getMimeBoundary()
263
    {
264
        if ($this->mimeBoundary === false) {
265
            $this->mimeBoundary = null;
266
            $contentType = $this->getContentType();
267
            if ($contentType !== null) {
268
                $this->mimeBoundary = $contentType->getValueFor('boundary');
269
            }
270
        }
271
        return $this->mimeBoundary;
272
    }
273
    
274
    /**
275
     * Returns true if this part's content-type is multipart/*
276
     *
277
     * @return boolean
278
     */
279
    public function isMultiPart()
280
    {
281
        $contentType = $this->getContentType();
282
        if ($contentType !== null) {
283
            // casting to bool, preg_match returns 1 for true
284
            return (bool) (preg_match(
285
                '~multipart/\w+~i',
286
                $contentType->getValue()
287
            ));
288
        }
289
        return false;
290
    }
291
    
292
    /**
293
     * Returns true if the passed $line of read input matches this PartBuilder's
294
     * mime boundary, or any of its parent's mime boundaries for a multipart
295
     * message.
296
     * 
297
     * If the passed $line is the ending boundary for the current PartBuilder,
298
     * $this->isEndBoundaryFound will return true after.
299
     * 
300
     * @param string $line
301
     * @return boolean
302
     */
303
    public function setEndBoundary($line)
304
    {
305
        $boundary = $this->getMimeBoundary();
306
        if ($boundary !== null) {
307
            if ($line === "--$boundary--") {
308
                $this->endBoundaryFound = true;
309
                return true;
310
            } elseif ($line === "--$boundary") {
311
                return true;
312
            }
313
        } elseif ($this->getParent() !== null && $this->getParent()->setEndBoundary($line)) {
314
            return true;
315
        }
316
        return false;
317
    }
318
    
319
    /**
320
     * Returns true if MessageParser passed an input line to setEndBoundary that
321
     * indicates the end of the part.
322
     * 
323
     * @return boolean
324
     */
325
    public function isEndBoundaryFound()
326
    {
327
        return $this->endBoundaryFound;
328
    }
329
    
330
    /**
331
     * Constructs and returns a filename where the part can be read from the
332
     * passed $messageObjectId.
333
     * 
334
     * @param string $messageObjectId the message object id
335
     * @return string
336
     */
337
    public function getStreamPartFilename($messageObjectId)
338
    {
339
        if ($this->streamPartEndPos === 0) {
340
            return null;
341
        }
342
        return $this->streamWrapperProtocol . '://' . $messageObjectId
343
            . '?start=' . $this->streamPartStartPos . '&end='
344
            . $this->streamPartEndPos;
345
    }
346
    
347
    /**
348
     * Constructs and returns a filename where the part's content can be read
349
     * from the passed $messageObjectId.
350
     * 
351
     * @param string $messageObjectId the message object id
352
     * @return string
353
     */
354
    public function getStreamContentFilename($messageObjectId)
355
    {
356
        if ($this->streamContentEndPos === 0) {
357
            return null;
358
        }
359
        return $this->streamWrapperProtocol . '://' . $messageObjectId
360
            . '?start=' . $this->streamContentStartPos . '&end='
361
            . $this->streamContentEndPos;
362
    }
363
    
364
    /**
365
     * Sets the start position of the part in the input stream.
366
     * 
367
     * @param int $streamPartStartPos
368
     */
369
    public function setStreamPartStartPos($streamPartStartPos)
370
    {
371
        $this->streamPartStartPos = $streamPartStartPos;
372
    }
373
374
    /**
375
     * Sets the end position of the part in the input stream.
376
     * 
377
     * @param int $streamPartEndPos
378
     */
379
    public function setStreamPartEndPos($streamPartEndPos)
380
    {
381
        $this->streamPartEndPos = $streamPartEndPos;
382
    }
383
384
    /**
385
     * Sets the start position of the content in the input stream.
386
     * 
387
     * @param int $streamContentStartPos
388
     */
389
    public function setStreamContentStartPos($streamContentStartPos)
390
    {
391
        $this->streamContentStartPos = $streamContentStartPos;
392
    }
393
394
    /**
395
     * Sets the end position of the content and part in the input stream.
396
     * 
397
     * @param int $streamContentEndPos
398
     */
399
    public function setStreamPartAndContentEndPos($streamContentEndPos)
400
    {
401
        $this->streamContentEndPos = $streamContentEndPos;
402
        $this->streamPartEndPos = $streamContentEndPos;
403
    }
404
    
405
    /**
406
     * Creates a MessagePart and returns it using the PartBuilder's
407
     * MessagePartFactory passed in during construction.
408
     * 
409
     * @param string $messageObjectId
410
     * @return MessagePart
411
     */
412
    public function createMessagePart($messageObjectId)
413
    {
414
        return $this->messagePartFactory->newInstance(
415
            $messageObjectId,
416
            $this
417
        );
418
    }
419
}
420