Test Failed
Push — 2.0 ( 9e8731...0b4092 )
by Zaahid
03:06
created

ParserPartStreamContainer::__destruct()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 2
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 4
rs 10
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\Parser\Part;
8
9
use ZBateson\MailMimeParser\Message\PartStreamContainer;
10
use ZBateson\MailMimeParser\Parser\Proxy\ParserPartProxy;
11
use ZBateson\MailMimeParser\Stream\StreamFactory;
12
use Psr\Http\Message\StreamInterface;
13
use SplObserver;
14
use SplSubject;
15
16
/**
17
 * A part stream container that proxies requests for content streams to a parser
18
 * to read the content.
19
 *
20
 * Keeps reference to the original stream a message was parsed from, using that
21
 * stream as the message's stream instead of the parent's MessagePartStream
22
 * unless the part changed.
23
 * 
24
 * The container must also be attached to its underlying part with
25
 * SplSubject::attach() so the ParserPartStreamContainer gets notified of any
26
 * changes.
27
 *
28
 * @author Zaahid Bateson
29
 */
30
class ParserPartStreamContainer extends PartStreamContainer implements SplObserver
31
{
32
    /**
33
     * @var ParserPartProxy
34
     */
35
    protected $parserProxy;
36
37
    /**
38
     * @var StreamInterface the original stream for a parsed message, used when
39
     *      the message hasn't changed
40
     */
41
    protected $parsedStream;
42
43
    /**
44
     * @var bool true if the stream should be detached when this container is
45
     *      destroyed.
46
     */
47
    protected $detachParsedStream = false;
48
49
    /**
50
     * @var bool set to true if the part's been updated since it was created.
51
     */
52
    protected $partUpdated = false;
53
54
    /**
55
     * @var bool false if the content for the part represented by this container
56
     *      has not yet been requested from the parser.
57
     */
58
    protected $contentParseRequested = false;
59
60
    public function __construct(StreamFactory $streamFactory, ParserPartProxy $parserProxy)
61
    {
62
        parent::__construct($streamFactory);
63
        $this->parserProxy = $parserProxy;
64
    }
65
66
    public function __destruct()
67
    {
68
        if ($this->detachParsedStream && $this->parsedStream !== null) {
69
            $this->parsedStream->detach();
70
        }
71
    }
72
73
    /**
74
     * Requests content from the parser if not previously requested, and call
75
     * PartStreamContainer::setContentStream().
76
     */
77
    protected function requestParsedContentStream()
78
    {
79
        if (!$this->contentParseRequested) {
80
            $this->contentParseRequested = true;
81
            $this->parserProxy->parseContent();
82
            parent::setContentStream($this->streamFactory->getLimitedContentStream(
83
                $this->parserProxy->getPartBuilder()
84
            ));
85
        }
86
    }
87
88
    /**
89
     * Ensures the parser has parsed the entire part, and sets
90
     * $this->parsedStream to the original parsed stream (or a limited part of
91
     * it corresponding to the current part this stream container belongs to).
92
     */
93
    protected function requestParsedStream()
94
    {
95
        if ($this->parsedStream === null) {
96
            $this->parserProxy->parseAll();
97
            $this->parsedStream = $this->streamFactory->getLimitedPartStream(
98
                $this->parserProxy->getPartBuilder()
99
            );
100
            if ($this->parsedStream !== null) {
101
                $this->detachParsedStream = ($this->parsedStream->getMetadata('mmp-detached-stream') === true);
102
            }
103
        }
104
    }
105
106
    public function hasContent()
107
    {
108
        $this->requestParsedContentStream();
109
        return parent::hasContent();
110
    }
111
112
    public function getContentStream($transferEncoding, $fromCharset, $toCharset)
113
    {
114
        $this->requestParsedContentStream();
115
        return parent::getContentStream($transferEncoding, $fromCharset, $toCharset);
116
    }
117
118
    public function getBinaryContentStream($transferEncoding)
119
    {
120
        $this->requestParsedContentStream();
121
        return parent::getBinaryContentStream($transferEncoding);
122
    }
123
124
    public function setContentStream(StreamInterface $contentStream = null)
125
    {
126
        // has to be overridden because requestParsedContentStream calls
127
        // parent::setContentStream as well, so needs to be parsed before
128
        // overriding the contentStream with a manual 'set'.
129
        $this->requestParsedContentStream();
130
        parent::setContentStream($contentStream);
131
    }
132
133
    public function getStream()
134
    {
135
        $this->requestParsedStream();
136
        if (!$this->partUpdated) {
137
            if ($this->parsedStream !== null) {
138
                $this->parsedStream->rewind();
139
                return $this->parsedStream;
140
            }
141
        }
142
        return parent::getStream();
143
    }
144
145
    public function update(SplSubject $subject)
146
    {
147
        $this->partUpdated = true;
148
    }
149
}
150