Test Failed
Push — 1.0.0 ( d3c3e6...4505d9 )
by Zaahid
04:05
created

PartBuilder::getStreamPartStartOffset()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

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