Passed
Push — master ( 9e6d2f...c6b62f )
by Zaahid
06:54
created

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