Passed
Branch php8-testing (0e47ea)
by Zaahid
03:09
created

PartBuilder::getChildren()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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\Message\Part;
8
9
use Psr\Http\Message\StreamInterface;
10
use ZBateson\MailMimeParser\Header\HeaderContainer;
11
use ZBateson\MailMimeParser\Message\Part\Factory\MessagePartFactory;
12
13
/**
14
 * Used by MessageParser to keep information about a parsed message as an
15
 * intermediary before creating a Message object and its MessagePart children.
16
 *
17
 * @author Zaahid Bateson
18
 */
19
class PartBuilder
20
{
21
    /**
22
     * @var int The offset read start position for this part (beginning of
23
     * headers) in the message's stream.
24
     */
25
    private $streamPartStartPos = 0;
26
    
27
    /**
28
     * @var int The offset read end position for this part.  If the part is a
29
     * multipart mime part, the end position is after all of this parts
30
     * children.
31
     */
32
    private $streamPartEndPos = 0;
33
    
34
    /**
35
     * @var int The offset read start position in the message's stream for the
36
     * beginning of this part's content (body).
37
     */
38
    private $streamContentStartPos = 0;
39
    
40
    /**
41
     * @var int The offset read end position in the message's stream for the
42
     * end of this part's content (body).
43
     */
44
    private $streamContentEndPos = 0;
45
46
    /**
47
     * @var MessagePartFactory the factory
48
     *      needed for creating the Message or MessagePart for the parsed part.
49
     */
50
    private $messagePartFactory;
51
    
52
    /**
53
     * @var boolean set to true once the end boundary of the currently-parsed
54
     *      part is found.
55
     */
56
    private $endBoundaryFound = false;
57
    
58
    /**
59
     * @var boolean set to true once a boundary belonging to this parent's part
60
     *      is found.
61
     */
62
    private $parentBoundaryFound = false;
63
    
64
    /**
65
     * @var boolean|null|string false if not queried for in the content-type
66
     *      header of this part, null if the current part does not have a
67
     *      boundary, or the value of the boundary parameter of the content-type
68
     *      header if the part contains one.
69
     */
70
    private $mimeBoundary = false;
71
    
72
    /**
73
     * @var HeaderContainer a container for found and parsed headers.
74
     */
75
    private $headerContainer;
76
    
77
    /**
78
     * @var PartBuilder[] an array of children found below this part for a mime
79
     *      email
80
     */
81
    private $children = [];
82
    
83
    /**
84
     * @var PartBuilder the parent part.
85
     */
86
    private $parent = null;
87
    
88
    /**
89
     * @var string[] key => value pairs of properties passed on to the 
90
     *      $messagePartFactory when constructing the Message and its children.
91
     */
92
    private $properties = [];
93
94
    /**
95
     * Sets up class dependencies.
96
     *
97
     * @param MessagePartFactory $mpf
98
     * @param HeaderContainer $headerContainer
99
     */
100 119
    public function __construct(
101
        MessagePartFactory $mpf,
102
        HeaderContainer $headerContainer
103
    ) {
104 119
        $this->messagePartFactory = $mpf;
105 119
        $this->headerContainer = $headerContainer;
106 119
    }
107
    
108
    /**
109
     * Adds a header with the given $name and $value to the headers array.
110
     *
111
     * Removes non-alphanumeric characters from $name, and sets it to lower-case
112
     * to use as a key in the private headers array.  Sets the original $name
113
     * and $value as elements in the headers' array value for the calculated
114
     * key.
115
     *
116
     * @param string $name
117
     * @param string $value
118
     */
119 106
    public function addHeader($name, $value)
120
    {
121 106
        $this->headerContainer->add($name, $value);
122 106
    }
123
    
124
    /**
125
     * Returns the HeaderContainer object containing parsed headers.
126
     * 
127
     * @return HeaderContainer
128
     */
129 105
    public function getHeaderContainer()
130
    {
131 105
        return $this->headerContainer;
132
    }
133
    
134
    /**
135
     * Sets the specified property denoted by $name to $value.
136
     * 
137
     * @param string $name
138
     * @param mixed $value
139
     */
140 4
    public function setProperty($name, $value)
141
    {
142 4
        $this->properties[$name] = $value;
143 4
    }
144
    
145
    /**
146
     * Returns the value of the property with the given $name.
147
     * 
148
     * @param string $name
149
     * @return mixed
150
     */
151 4
    public function getProperty($name)
152
    {
153 4
        if (!isset($this->properties[$name])) {
154 1
            return null;
155
        }
156 4
        return $this->properties[$name];
157
    }
158
    
159
    /**
160
     * Registers the passed PartBuilder as a child of the current PartBuilder.
161
     * 
162
     * @param \ZBateson\MailMimeParser\Message\PartBuilder $partBuilder
0 ignored issues
show
Bug introduced by
The type ZBateson\MailMimeParser\Message\PartBuilder 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...
163
     */
164 79
    public function addChild(PartBuilder $partBuilder)
165
    {
166 79
        $partBuilder->parent = $this;
167
        // discard parts added after the end boundary
168 79
        if (!$this->endBoundaryFound) {
169 78
            $this->children[] = $partBuilder;
170
        }
171 79
    }
172
    
173
    /**
174
     * Returns all children PartBuilder objects.
175
     * 
176
     * @return \ZBateson\MailMimeParser\Message\PartBuilder[]
177
     */
178 106
    public function getChildren()
179
    {
180 106
        return $this->children;
181
    }
182
    
183
    /**
184
     * Returns this PartBuilder's parent.
185
     * 
186
     * @return PartBuilder
187
     */
188 108
    public function getParent()
189
    {
190 108
        return $this->parent;
191
    }
192
    
193
    /**
194
     * Returns true if either a Content-Type or Mime-Version header are defined
195
     * in this PartBuilder's headers.
196
     * 
197
     * @return boolean
198
     */
199 105
    public function isMime()
200
    {
201 105
        return ($this->headerContainer->exists('Content-Type') ||
202 105
            $this->headerContainer->exists('Mime-Version'));
203
    }
204
    
205
    /**
206
     * Returns a ParameterHeader representing the parsed Content-Type header for
207
     * this PartBuilder.
208
     * 
209
     * @return \ZBateson\MailMimeParser\Header\ParameterHeader
210
     */
211 108
    public function getContentType()
212
    {
213 108
        return $this->headerContainer->get('Content-Type');
214
    }
215
    
216
    /**
217
     * Returns the parsed boundary parameter of the Content-Type header if set
218
     * for a multipart message part.
219
     * 
220
     * @return string
221
     */
222 106
    public function getMimeBoundary()
223
    {
224 106
        if ($this->mimeBoundary === false) {
225 106
            $this->mimeBoundary = null;
226 106
            $contentType = $this->getContentType();
227 106
            if ($contentType !== null) {
228 105
                $this->mimeBoundary = $contentType->getValueFor('boundary');
229
            }
230
        }
231 106
        return $this->mimeBoundary;
232
    }
233
    
234
    /**
235
     * Returns true if this part's content-type is multipart/*
236
     *
237
     * @return boolean
238
     */
239 102
    public function isMultiPart()
240
    {
241 102
        $contentType = $this->getContentType();
242 102
        if ($contentType !== null) {
243
            // casting to bool, preg_match returns 1 for true
244 101
            return (bool) (preg_match(
245 101
                '~multipart/.*~i',
246 101
                $contentType->getValue()
247
            ));
248
        }
249 73
        return false;
250
    }
251
    
252
    /**
253
     * Returns true if the passed $line of read input matches this PartBuilder's
254
     * mime boundary, or any of its parent's mime boundaries for a multipart
255
     * message.
256
     * 
257
     * If the passed $line is the ending boundary for the current PartBuilder,
258
     * $this->isEndBoundaryFound will return true after.
259
     * 
260
     * @param string $line
261
     * @return boolean
262
     */
263 105
    public function setEndBoundaryFound($line)
264
    {
265 105
        $boundary = $this->getMimeBoundary();
266 105
        if ($this->parent !== null && $this->parent->setEndBoundaryFound($line)) {
267 73
            $this->parentBoundaryFound = true;
268 73
            return true;
269 105
        } elseif ($boundary !== null) {
0 ignored issues
show
introduced by
The condition $boundary !== null is always true.
Loading history...
270 76
            if ($line === "--$boundary--") {
271 74
                $this->endBoundaryFound = true;
272 74
                return true;
273 75
            } elseif ($line === "--$boundary") {
274 74
                return true;
275
            }
276
        }
277 104
        return false;
278
    }
279
    
280
    /**
281
     * Returns true if MessageParser passed an input line to setEndBoundary that
282
     * matches a parent's mime boundary, and the following input belongs to a
283
     * new part under its parent.
284
     * 
285
     * @return boolean
286
     */
287 75
    public function isParentBoundaryFound()
288
    {
289 75
        return ($this->parentBoundaryFound);
290
    }
291
    
292
    /**
293
     * Called once EOF is reached while reading content.  The method sets the
294
     * flag used by PartBuilder::isParentBoundaryFound to true on this part and
295
     * all parent PartBuilders.
296
     */
297 102
    public function setEof()
298
    {
299 102
        $this->parentBoundaryFound = true;
300 102
        if ($this->parent !== null) {
301 73
            $this->parent->parentBoundaryFound = true;
302
        }
303 102
    }
304
    
305
    /**
306
     * Returns false if this part has a parent part in which endBoundaryFound is
307
     * set to true (i.e. this isn't a discardable part following the parent's
308
     * end boundary line).
309
     * 
310
     * @return boolean
311
     */
312 106
    public function canHaveHeaders()
313
    {
314 106
        return ($this->parent === null || !$this->parent->endBoundaryFound);
315
    }
316
317
    /**
318
     * Returns the offset for this part's stream within its parent stream.
319
     *
320
     * @return int
321
     */
322 76
    public function getStreamPartStartOffset()
323
    {
324 76
        if ($this->parent) {
325 74
            return $this->streamPartStartPos - $this->parent->streamPartStartPos;
326
        }
327 3
        return $this->streamPartStartPos;
328
    }
329
330
    /**
331
     * Returns the length of this part's stream.
332
     *
333
     * @return int
334
     */
335 76
    public function getStreamPartLength()
336
    {
337 76
        return $this->streamPartEndPos - $this->streamPartStartPos;
338
    }
339
340
    /**
341
     * Returns the offset for this part's content within its part stream.
342
     *
343
     * @return int
344
     */
345 106
    public function getStreamContentStartOffset()
346
    {
347 106
        if ($this->parent) {
348 74
            return $this->streamContentStartPos - $this->parent->streamPartStartPos;
349
        }
350 79
        return $this->streamContentStartPos;
351
    }
352
353
    /**
354
     * Returns the length of this part's content stream.
355
     *
356
     * @return int
357
     */
358 106
    public function getStreamContentLength()
359
    {
360 106
        return $this->streamContentEndPos - $this->streamContentStartPos;
361
    }
362
363
    /**
364
     * Sets the start position of the part in the input stream.
365
     * 
366
     * @param int $streamPartStartPos
367
     */
368 107
    public function setStreamPartStartPos($streamPartStartPos)
369
    {
370 107
        $this->streamPartStartPos = $streamPartStartPos;
371 107
    }
372
373
    /**
374
     * Sets the end position of the part in the input stream, and also calls
375
     * parent->setParentStreamPartEndPos to expand to parent parts.
376
     * 
377
     * @param int $streamPartEndPos
378
     */
379 107
    public function setStreamPartEndPos($streamPartEndPos)
380
    {
381 107
        $this->streamPartEndPos = $streamPartEndPos;
382 107
        if ($this->parent !== null) {
383 74
            $this->parent->setStreamPartEndPos($streamPartEndPos);
384
        }
385 107
    }
386
387
    /**
388
     * Sets the start position of the content in the input stream.
389
     * 
390
     * @param int $streamContentStartPos
391
     */
392 106
    public function setStreamContentStartPos($streamContentStartPos)
393
    {
394 106
        $this->streamContentStartPos = $streamContentStartPos;
395 106
    }
396
397
    /**
398
     * Sets the end position of the content and part in the input stream.
399
     * 
400
     * @param int $streamContentEndPos
401
     */
402 106
    public function setStreamPartAndContentEndPos($streamContentEndPos)
403
    {
404 106
        $this->streamContentEndPos = $streamContentEndPos;
405 106
        $this->setStreamPartEndPos($streamContentEndPos);
406 106
    }
407
408
    /**
409
     * Creates a MessagePart and returns it using the PartBuilder's
410
     * MessagePartFactory passed in during construction.
411
     * 
412
     * @param StreamInterface $stream
413
     * @return MessagePart
414
     */
415 105
    public function createMessagePart(StreamInterface $stream = null)
416
    {
417 105
        return $this->messagePartFactory->newInstance(
418 105
            $this,
419 105
            $stream
420
        );
421
    }
422
}
423