zbateson /
mail-mime-parser
| 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\Stream; |
||||
| 9 | |||||
| 10 | use GuzzleHttp\Psr7; |
||||
| 11 | use GuzzleHttp\Psr7\AppendStream; |
||||
| 12 | use Psr\Http\Message\StreamInterface; |
||||
| 13 | use SplObserver; |
||||
| 14 | use SplSubject; |
||||
| 15 | use ZBateson\MailMimeParser\Header\HeaderConsts; |
||||
| 16 | use ZBateson\MailMimeParser\MailMimeParser; |
||||
| 17 | use ZBateson\MailMimeParser\Message\IMessagePart; |
||||
| 18 | use ZBateson\MailMimeParser\Message\IMimePart; |
||||
| 19 | use ZBateson\MbWrapper\UnsupportedCharsetException; |
||||
| 20 | |||||
| 21 | /** |
||||
| 22 | * Provides a readable stream for a MessagePart. |
||||
| 23 | * |
||||
| 24 | * @author Zaahid Bateson |
||||
| 25 | */ |
||||
| 26 | class MessagePartStream extends MessagePartStreamDecorator implements SplObserver, StreamInterface |
||||
| 27 | { |
||||
| 28 | /** |
||||
| 29 | * @var StreamFactory For creating needed stream decorators. |
||||
| 30 | */ |
||||
| 31 | protected StreamFactory $streamFactory; |
||||
| 32 | |||||
| 33 | /** |
||||
| 34 | * @var IMessagePart The part to read from. |
||||
| 35 | */ |
||||
| 36 | protected IMessagePart $part; |
||||
| 37 | |||||
| 38 | /** |
||||
| 39 | * @var bool if false, saving a content stream with an unsupported charset |
||||
| 40 | * will be written in the default charset, otherwise the stream will be |
||||
| 41 | * created with the unsupported charset, and an exception will be |
||||
| 42 | * thrown when read from. |
||||
| 43 | */ |
||||
| 44 | protected bool $throwExceptionReadingPartContentFromUnsupportedCharsets; |
||||
| 45 | |||||
| 46 | /** |
||||
| 47 | * @var ?AppendStream |
||||
| 48 | */ |
||||
| 49 | protected ?AppendStream $appendStream = null; |
||||
| 50 | |||||
| 51 | 113 | public function __construct(StreamFactory $sdf, IMessagePart $part, bool $throwExceptionReadingPartContentFromUnsupportedCharsets) |
|||
| 52 | { |
||||
| 53 | 113 | parent::__construct($part); |
|||
| 54 | 113 | $this->streamFactory = $sdf; |
|||
| 55 | 113 | $this->part = $part; |
|||
| 56 | 113 | $this->throwExceptionReadingPartContentFromUnsupportedCharsets = $throwExceptionReadingPartContentFromUnsupportedCharsets; |
|||
| 57 | 113 | $part->attach($this); |
|||
| 58 | |||||
| 59 | // unsetting the property forces the first access to go through |
||||
| 60 | // __get(). |
||||
| 61 | 113 | unset($this->stream); |
|||
| 62 | } |
||||
| 63 | |||||
| 64 | 11 | public function __destruct() |
|||
| 65 | { |
||||
| 66 | 11 | $this->part->detach($this); |
|||
| 67 | } |
||||
| 68 | |||||
| 69 | 96 | public function update(SplSubject $subject) : void |
|||
| 70 | { |
||||
| 71 | 96 | if ($this->appendStream !== null) { |
|||
| 72 | // unset forces recreation in StreamDecoratorTrait with a call to __get |
||||
| 73 | 3 | unset($this->stream); |
|||
| 74 | 3 | $this->appendStream = null; |
|||
| 75 | } |
||||
| 76 | } |
||||
| 77 | |||||
| 78 | /** |
||||
| 79 | * Attaches and returns a CharsetStream decorator to the passed $stream. |
||||
| 80 | * |
||||
| 81 | * If the current attached IMessagePart doesn't specify a charset, $stream |
||||
| 82 | * is returned as-is. |
||||
| 83 | */ |
||||
| 84 | 100 | private function getCharsetDecoratorForStream(StreamInterface $stream) : StreamInterface |
|||
| 85 | { |
||||
| 86 | 100 | $charset = $this->part->getCharset(); |
|||
| 87 | 100 | if (!empty($charset)) { |
|||
| 88 | 79 | if (!$this->throwExceptionReadingPartContentFromUnsupportedCharsets) { |
|||
| 89 | 77 | $test = $this->streamFactory->newCharsetStream( |
|||
| 90 | 77 | Psr7\Utils::streamFor(), |
|||
| 91 | 77 | $charset, |
|||
| 92 | 77 | MailMimeParser::DEFAULT_CHARSET |
|||
| 93 | 77 | ); |
|||
| 94 | try { |
||||
| 95 | 77 | $test->write('t'); |
|||
| 96 | 2 | } catch (UnsupportedCharsetException $e) { |
|||
| 97 | 2 | return $stream; |
|||
| 98 | } finally { |
||||
| 99 | 77 | $test->close(); |
|||
| 100 | } |
||||
| 101 | } |
||||
| 102 | 77 | $stream = $this->streamFactory->newCharsetStream( |
|||
| 103 | 77 | $stream, |
|||
| 104 | 77 | $charset, |
|||
| 105 | 77 | MailMimeParser::DEFAULT_CHARSET |
|||
| 106 | 77 | ); |
|||
| 107 | } |
||||
| 108 | 98 | return $stream; |
|||
| 109 | } |
||||
| 110 | |||||
| 111 | /** |
||||
| 112 | * Creates an array of streams based on the attached part's mime boundary |
||||
| 113 | * and child streams. |
||||
| 114 | * |
||||
| 115 | * @param IMimePart $part passed in because $this->part is declared |
||||
| 116 | * as IMessagePart |
||||
| 117 | * @return StreamInterface[] |
||||
| 118 | */ |
||||
| 119 | 67 | protected function getBoundaryAndChildStreams(IMimePart $part) : array |
|||
| 120 | { |
||||
| 121 | 67 | $boundary = $part->getHeaderParameter(HeaderConsts::CONTENT_TYPE, 'boundary'); |
|||
| 122 | 67 | if ($boundary === null) { |
|||
| 123 | 2 | return \array_map( |
|||
| 124 | 2 | function($child) { |
|||
|
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||||
| 125 | 2 | return $child->getStream(); |
|||
| 126 | 2 | }, |
|||
| 127 | 2 | $part->getChildParts() |
|||
| 128 | 2 | ); |
|||
| 129 | } |
||||
| 130 | 65 | $streams = []; |
|||
| 131 | 65 | foreach ($part->getChildParts() as $i => $child) { |
|||
| 132 | 65 | if ($i !== 0 || $part->hasContent()) { |
|||
| 133 | 65 | $streams[] = Psr7\Utils::streamFor("\r\n"); |
|||
| 134 | } |
||||
| 135 | 65 | $streams[] = Psr7\Utils::streamFor("--$boundary\r\n"); |
|||
| 136 | 65 | $streams[] = $child->getStream(); |
|||
| 137 | } |
||||
| 138 | 65 | $streams[] = Psr7\Utils::streamFor("\r\n--$boundary--\r\n"); |
|||
| 139 | |||||
| 140 | 65 | return $streams; |
|||
| 141 | } |
||||
| 142 | |||||
| 143 | /** |
||||
| 144 | * Returns an array of Psr7 Streams representing the attached part and it's |
||||
| 145 | * direct children. |
||||
| 146 | * |
||||
| 147 | * @return StreamInterface[] |
||||
| 148 | */ |
||||
| 149 | 100 | protected function getStreamsArray() : array |
|||
| 150 | { |
||||
| 151 | 100 | $contentStream = $this->part->getContentStream(); |
|||
| 152 | 100 | if ($contentStream !== null) { |
|||
| 153 | // wrapping in a SeekingLimitStream because the underlying |
||||
| 154 | // ContentStream could be rewound, etc... |
||||
| 155 | 100 | $contentStream = $this->streamFactory->newDecoratedCachingStream( |
|||
| 156 | 100 | $this->streamFactory->newSeekingStream($contentStream), |
|||
| 157 | 100 | function($stream) { |
|||
|
0 ignored issues
–
show
|
|||||
| 158 | 100 | $es = $this->streamFactory->getTransferEncodingDecoratedStream( |
|||
| 159 | 100 | $stream, |
|||
| 160 | 100 | $this->part->getContentTransferEncoding(), |
|||
| 161 | 100 | $this->part->getFilename() |
|||
| 162 | 100 | ); |
|||
| 163 | 100 | $cs = $this->getCharsetDecoratorForStream($es); |
|||
| 164 | 100 | return $cs; |
|||
| 165 | 100 | } |
|||
| 166 | 100 | ); |
|||
| 167 | } |
||||
| 168 | |||||
| 169 | 100 | $streams = [$this->streamFactory->newHeaderStream($this->part), $contentStream ?: Psr7\Utils::streamFor()]; |
|||
| 170 | |||||
| 171 | 100 | if ($this->part instanceof IMimePart && $this->part->getChildCount() > 0) { |
|||
|
0 ignored issues
–
show
The method
getChildCount() does not exist on ZBateson\MailMimeParser\Message\IMessagePart. It seems like you code against a sub-type of ZBateson\MailMimeParser\Message\IMessagePart such as ZBateson\MailMimeParser\Message\IMultiPart or ZBateson\MailMimeParser\Message\MultiPart.
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 172 | 67 | $streams = \array_merge($streams, $this->getBoundaryAndChildStreams($this->part)); |
|||
| 173 | } |
||||
| 174 | |||||
| 175 | 100 | return $streams; |
|||
| 176 | } |
||||
| 177 | |||||
| 178 | /** |
||||
| 179 | * Creates the underlying stream lazily when required. |
||||
| 180 | */ |
||||
| 181 | 100 | protected function createStream() : StreamInterface |
|||
| 182 | { |
||||
| 183 | 100 | if ($this->appendStream === null) { |
|||
| 184 | 100 | $this->appendStream = new AppendStream($this->getStreamsArray()); |
|||
| 185 | } |
||||
| 186 | 100 | return $this->appendStream; |
|||
| 187 | } |
||||
| 188 | } |
||||
| 189 |