requestParsedContentStream()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 6
c 0
b 0
f 0
dl 0
loc 10
ccs 8
cts 8
cp 1
rs 10
cc 2
nc 2
nop 0
crap 2
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
8
namespace ZBateson\MailMimeParser\Parser\Part;
9
10
use Psr\Http\Message\StreamInterface;
11
use Psr\Log\LoggerInterface;
12
use SplObserver;
13
use SplSubject;
14
use ZBateson\MailMimeParser\Message\IMessagePart;
15
use ZBateson\MailMimeParser\Message\PartStreamContainer;
16
use ZBateson\MailMimeParser\Parser\Proxy\ParserPartProxy;
17
use ZBateson\MailMimeParser\Stream\MessagePartStreamDecorator;
18
use ZBateson\MailMimeParser\Stream\StreamFactory;
19
use ZBateson\MbWrapper\MbWrapper;
20
21
/**
22
 * A part stream container that proxies requests for content streams to a parser
23
 * to read the content.
24
 *
25
 * Keeps reference to the original stream a part was parsed from, using that
26
 * stream as the part's stream instead of the PartStreamContainer's
27
 * MessagePartStream (which dynamically creates a stream from an IMessagePart)
28
 * unless the part changed.
29
 *
30
 * The ParserPartStreamContainer must also be attached to its underlying part
31
 * with SplSubject::attach() so the ParserPartStreamContainer gets notified of
32
 * any changes.
33
 *
34
 * @author Zaahid Bateson
35
 */
36
class ParserPartStreamContainer extends PartStreamContainer implements SplObserver
37
{
38
    /**
39
     * @var ParserPartProxy The parser proxy to ferry requests to on-demand.
40
     */
41
    protected ParserPartProxy $parserProxy;
42
43
    /**
44
     * @var MessagePartStreamDecorator the original stream for a parsed message,
45
     *      wrapped in a MessagePartStreamDecorator, and used when the message
46
     *      hasn't changed
47
     */
48
    protected ?MessagePartStreamDecorator $parsedStream = null;
49
50
    /**
51
     * @var bool set to true if the part's been updated since it was created.
52
     */
53
    protected bool $partUpdated = false;
54
55
    /**
56
     * @var bool false if the content for the part represented by this container
57
     *      has not yet been requested from the parser.
58
     */
59
    protected bool $contentParseRequested = false;
60
61 118
    public function __construct(
62
        LoggerInterface $logger,
63
        StreamFactory $streamFactory,
64
        MbWrapper $mbWrapper,
65
        bool $throwExceptionReadingPartContentFromUnsupportedCharsets,
66
        ParserPartProxy $parserProxy
67
    ) {
68 118
        parent::__construct($logger, $streamFactory, $mbWrapper, $throwExceptionReadingPartContentFromUnsupportedCharsets);
69 118
        $this->parserProxy = $parserProxy;
70
    }
71
72 7
    public function __destruct()
73
    {
74 7
        if ($this->detachParsedStream && $this->parsedStream !== null) {
75 2
            $this->parsedStream->detach();
76
        }
77
    }
78
79
    /**
80
     * Requests content from the parser if not previously requested, and calls
81
     * PartStreamContainer::setContentStream().
82
     */
83 112
    protected function requestParsedContentStream() : static
84
    {
85 112
        if (!$this->contentParseRequested) {
86 112
            $this->contentParseRequested = true;
87 112
            $this->parserProxy->parseContent();
88 112
            parent::setContentStream($this->streamFactory->getLimitedContentStream(
89 112
                $this->parserProxy
90 112
            ));
91
        }
92 112
        return $this;
93
    }
94
95
    /**
96
     * Ensures the parser has parsed the entire part, and sets
97
     * $this->parsedStream to the original parsed stream (or a limited part of
98
     * it corresponding to the current part this stream container belongs to).
99
     */
100 104
    protected function requestParsedStream() : static
101
    {
102 104
        if ($this->parsedStream === null) {
103 104
            $this->parserProxy->parseAll();
104 104
            $this->parsedStream = $this->streamFactory->newDecoratedMessagePartStream(
105 104
                $this->parserProxy->getPart(),
106 104
                $this->streamFactory->getLimitedPartStream(
107 104
                    $this->parserProxy
108 104
                )
109 104
            );
110 104
            if ($this->parsedStream !== null) {
111 104
                $this->detachParsedStream = ($this->parsedStream->getMetadata('mmp-detached-stream') === true);
0 ignored issues
show
Bug introduced by
The method getMetadata() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

111
                $this->detachParsedStream = ($this->parsedStream->/** @scrutinizer ignore-call */ getMetadata('mmp-detached-stream') === true);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
112
            }
113
        }
114 104
        return $this;
115
    }
116
117 107
    public function hasContent() : bool
118
    {
119 107
        $this->requestParsedContentStream();
120 107
        return parent::hasContent();
121
    }
122
123 105
    public function getContentStream(IMessagePart $part, ?string $transferEncoding, ?string $fromCharset, ?string $toCharset) : ?MessagePartStreamDecorator
124
    {
125 105
        $this->requestParsedContentStream();
126 105
        return parent::getContentStream($part, $transferEncoding, $fromCharset, $toCharset);
127
    }
128
129 71
    public function getBinaryContentStream(IMessagePart $part, ?string $transferEncoding = null) : ?MessagePartStreamDecorator
130
    {
131 71
        $this->requestParsedContentStream();
132 71
        return parent::getBinaryContentStream($part, $transferEncoding);
133
    }
134
135 22
    public function setContentStream(?StreamInterface $contentStream = null) : static
136
    {
137
        // has to be overridden because requestParsedContentStream calls
138
        // parent::setContentStream as well, so needs to be parsed before
139
        // overriding the contentStream with a manual 'set'.
140 22
        $this->requestParsedContentStream();
141 22
        parent::setContentStream($contentStream);
142 22
        return $this;
143
    }
144
145 104
    public function getStream() : MessagePartStreamDecorator
146
    {
147 104
        $this->requestParsedStream();
148 104
        if (!$this->partUpdated) {
149 25
            if ($this->parsedStream !== null) {
150 25
                $this->parsedStream->rewind();
151 25
                return $this->parsedStream;
152
            }
153
        }
154 95
        return parent::getStream();
155
    }
156
157 97
    public function update(SplSubject $subject) : void
158
    {
159 97
        $this->partUpdated = true;
160
    }
161
}
162