Passed
Push — master ( ad3faa...5c4918 )
by Zaahid
03:33
created

PartBuilder   A

Complexity

Total Complexity 41

Size/Duplication

Total Lines 381
Duplicated Lines 0 %

Test Coverage

Coverage 98.96%

Importance

Changes 0
Metric Value
eloc 75
dl 0
loc 381
ccs 95
cts 96
cp 0.9896
rs 9.1199
c 0
b 0
f 0
wmc 41

25 Methods

Rating   Name   Duplication   Size   Complexity  
A isMultiPart() 0 11 2
A setStreamPartEndPos() 0 5 2
A addChild() 0 6 2
A setStreamContentStartPos() 0 3 1
A getProperty() 0 6 2
A getStreamContentLength() 0 3 1
A addHeader() 0 3 1
A setProperty() 0 3 1
A setStreamPartAndContentEndPos() 0 4 1
A isParentBoundaryFound() 0 3 1
A setEof() 0 5 2
A isMime() 0 4 2
A getMimeBoundary() 0 10 3
A setEndBoundaryFound() 0 15 6
A getStreamContentStartOffset() 0 6 2
A setStreamPartStartPos() 0 3 1
A canHaveHeaders() 0 3 2
A getContentType() 0 3 1
A createMessagePart() 0 5 1
A getChildren() 0 3 1
A __construct() 0 6 1
A getParent() 0 3 1
A getStreamPartStartOffset() 0 6 2
A getHeaderContainer() 0 3 1
A getStreamPartLength() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like PartBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PartBuilder, and based on these observations, apply Extract Interface, too.

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 15
    public function __construct(
101
        MessagePartFactory $mpf,
102
        HeaderContainer $headerContainer
103
    ) {
104 15
        $this->messagePartFactory = $mpf;
105 15
        $this->headerContainer = $headerContainer;
106 15
    }
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 2
    public function addHeader($name, $value)
120
    {
121 2
        $this->headerContainer->add($name, $value);
122 2
    }
123
    
124
    /**
125
     * Returns the HeaderContainer object containing parsed headers.
126
     * 
127
     * @return array
128
     */
129 1
    public function getHeaderContainer()
130
    {
131 1
        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 1
    public function setProperty($name, $value)
141
    {
142 1
        $this->properties[$name] = $value;
143 1
    }
144
    
145
    /**
146
     * Returns the value of the property with the given $name.
147
     * 
148
     * @param string $name
149
     * @return mixed
150
     */
151 1
    public function getProperty($name)
152
    {
153 1
        if (!isset($this->properties[$name])) {
154 1
            return null;
155
        }
156 1
        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 6
    public function addChild(PartBuilder $partBuilder)
165
    {
166 6
        $partBuilder->parent = $this;
167
        // discard parts added after the end boundary
168 6
        if (!$this->endBoundaryFound) {
169 5
            $this->children[] = $partBuilder;
170
        }
171 6
    }
172
    
173
    /**
174
     * Returns all children PartBuilder objects.
175
     * 
176
     * @return \ZBateson\MailMimeParser\Message\PartBuilder[]
177
     */
178 2
    public function getChildren()
179
    {
180 2
        return $this->children;
181
    }
182
    
183
    /**
184
     * Returns this PartBuilder's parent.
185
     * 
186
     * @return PartBuilder
187
     */
188 4
    public function getParent()
189
    {
190 4
        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 1
    public function isMime()
200
    {
201 1
        return ($this->headerContainer->exists('Content-Type') ||
202 1
            $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 7
    public function getContentType()
212
    {
213 7
        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 5
    public function getMimeBoundary()
223
    {
224 5
        if ($this->mimeBoundary === false) {
225 5
            $this->mimeBoundary = null;
226 5
            $contentType = $this->getContentType();
227 5
            if ($contentType !== null) {
228 5
                $this->mimeBoundary = $contentType->getValueFor('boundary');
229
            }
230
        }
231 5
        return $this->mimeBoundary;
232
    }
233
    
234
    /**
235
     * Returns true if this part's content-type is multipart/*
236
     *
237
     * @return boolean
238
     */
239 1
    public function isMultiPart()
240
    {
241 1
        $contentType = $this->getContentType();
242 1
        if ($contentType !== null) {
243
            // casting to bool, preg_match returns 1 for true
244 1
            return (bool) (preg_match(
245 1
                '~multipart/\w+~i',
246 1
                $contentType->getValue()
247
            ));
248
        }
249
        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 4
    public function setEndBoundaryFound($line)
264
    {
265 4
        $boundary = $this->getMimeBoundary();
266 4
        if ($this->parent !== null && $this->parent->setEndBoundaryFound($line)) {
267 1
            $this->parentBoundaryFound = true;
268 1
            return true;
269 4
        } elseif ($boundary !== null) {
0 ignored issues
show
introduced by
The condition $boundary !== null is always true.
Loading history...
270 4
            if ($line === "--$boundary--") {
271 2
                $this->endBoundaryFound = true;
272 2
                return true;
273 3
            } elseif ($line === "--$boundary") {
274 2
                return true;
275
            }
276
        }
277 3
        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 3
    public function isParentBoundaryFound()
288
    {
289 3
        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 1
    public function setEof()
298
    {
299 1
        $this->parentBoundaryFound = true;
300 1
        if ($this->parent !== null) {
301 1
            $this->parent->parentBoundaryFound = true;
302
        }
303 1
    }
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 2
    public function canHaveHeaders()
313
    {
314 2
        return ($this->parent === null || !$this->parent->endBoundaryFound);
315
    }
316
317 3
    public function getStreamPartStartOffset()
318
    {
319 3
        if ($this->parent) {
320 1
            return $this->streamPartStartPos - $this->parent->streamPartStartPos;
321
        }
322 3
        return $this->streamPartStartPos;
323
    }
324
    
325 3
    public function getStreamPartLength()
326
    {
327 3
        return $this->streamPartEndPos - $this->streamPartStartPos;
328
    }
329
330 2
    public function getStreamContentStartOffset()
331
    {
332 2
        if ($this->parent) {
333 1
            return $this->streamContentStartPos - $this->parent->streamPartStartPos;
334
        }
335 2
        return $this->streamContentStartPos;
336
    }
337
338 2
    public function getStreamContentLength()
339
    {
340 2
        return $this->streamContentEndPos - $this->streamContentStartPos;
341
    }
342
343
    /**
344
     * Sets the start position of the part in the input stream.
345
     * 
346
     * @param int $streamPartStartPos
347
     */
348 3
    public function setStreamPartStartPos($streamPartStartPos)
349
    {
350 3
        $this->streamPartStartPos = $streamPartStartPos;
351 3
    }
352
353
    /**
354
     * Sets the end position of the part in the input stream, and also calls
355
     * parent->setParentStreamPartEndPos to expand to parent parts.
356
     * 
357
     * @param int $streamPartEndPos
358
     */
359 3
    public function setStreamPartEndPos($streamPartEndPos)
360
    {
361 3
        $this->streamPartEndPos = $streamPartEndPos;
362 3
        if ($this->parent !== null) {
363 1
            $this->parent->setStreamPartEndPos($streamPartEndPos);
364
        }
365 3
    }
366
367
    /**
368
     * Sets the start position of the content in the input stream.
369
     * 
370
     * @param int $streamContentStartPos
371
     */
372 2
    public function setStreamContentStartPos($streamContentStartPos)
373
    {
374 2
        $this->streamContentStartPos = $streamContentStartPos;
375 2
    }
376
377
    /**
378
     * Sets the end position of the content and part in the input stream.
379
     * 
380
     * @param int $streamContentEndPos
381
     */
382 2
    public function setStreamPartAndContentEndPos($streamContentEndPos)
383
    {
384 2
        $this->streamContentEndPos = $streamContentEndPos;
385 2
        $this->setStreamPartEndPos($streamContentEndPos);
386 2
    }
387
388
    /**
389
     * Creates a MessagePart and returns it using the PartBuilder's
390
     * MessagePartFactory passed in during construction.
391
     * 
392
     * @param StreamInterface $stream
393
     * @return MessagePart
394
     */
395 1
    public function createMessagePart(StreamInterface $stream = null)
396
    {
397 1
        return $this->messagePartFactory->newInstance(
398 1
            $this,
399 1
            $stream
400
        );
401
    }
402
}
403