← All changes
|
vendor/zbateson/mail-mime-parser/src/Message/PartStreamContainer.php
+0
-303
trunk
→
11.1.1
View file →
| @@ -1,303 +1,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 | - | |
| 8 | -namespace ZBateson\MailMimeParser\Message; | |
| 9 | - | |
| 10 | -use GuzzleHttp\Psr7\CachingStream; | |
| 11 | -use Psr\Http\Message\StreamInterface; | |
| 12 | -use ZBateson\MailMimeParser\Stream\StreamFactory; | |
| 13 | - | |
| 14 | -/** | |
| 15 | - * Holds the stream and content stream objects for a part. | |
| 16 | - * | |
| 17 | - * Note that streams are not explicitly closed or detached on destruction of the | |
| 18 | - * PartSreamContainer by design: the passed StreamInterfaces will be closed on | |
| 19 | - * their destruction when no references to them remain, which is useful when the | |
| 20 | - * streams are passed around. | |
| 21 | - * | |
| 22 | - * In addition, all the streams passed to PartStreamContainer should be wrapping | |
| 23 | - * a ZBateson\StreamDecorators\NonClosingStream unless attached to a part by a | |
| 24 | - * user, this is because MMP uses a single seekable stream for content and wraps | |
| 25 | - * it in ZBateson\StreamDecorators\SeekingLimitStream objects for each part. | |
| 26 | - * | |
| 27 | - * @author Zaahid Bateson | |
| 28 | - */ | |
| 29 | -class PartStreamContainer | |
| 30 | -{ | |
| 31 | - /** | |
| 32 | - * @var StreamFactory used to apply psr7 stream decorators to the | |
| 33 | - * attached StreamInterface based on encoding. | |
| 34 | - */ | |
| 35 | - protected $streamFactory; | |
| 36 | - | |
| 37 | - /** | |
| 38 | - * @var StreamInterface stream containing the part's headers, content and | |
| 39 | - * children | |
| 40 | - */ | |
| 41 | - protected $stream; | |
| 42 | - | |
| 43 | - /** | |
| 44 | - * @var StreamInterface a stream containing this part's content | |
| 45 | - */ | |
| 46 | - protected $contentStream; | |
| 47 | - | |
| 48 | - /** | |
| 49 | - * @var StreamInterface the content stream after attaching transfer encoding | |
| 50 | - * streams to $contentStream. | |
| 51 | - */ | |
| 52 | - protected $decodedStream; | |
| 53 | - | |
| 54 | - /** | |
| 55 | - * @var StreamInterface attached charset stream to $decodedStream | |
| 56 | - */ | |
| 57 | - protected $charsetStream; | |
| 58 | - | |
| 59 | - /** | |
| 60 | - * @var bool true if the stream should be detached when this container is | |
| 61 | - * destroyed. | |
| 62 | - */ | |
| 63 | - protected $detachParsedStream; | |
| 64 | - | |
| 65 | - /** | |
| 66 | - * @var array<string, null> map of the active encoding filter on the current handle. | |
| 67 | - */ | |
| 68 | - private $encoding = [ | |
| 69 | - 'type' => null, | |
| 70 | - 'filter' => null | |
| 71 | - ]; | |
| 72 | - | |
| 73 | - /** | |
| 74 | - * @var array<string, null> map of the active charset filter on the current handle. | |
| 75 | - */ | |
| 76 | - private $charset = [ | |
| 77 | - 'from' => null, | |
| 78 | - 'to' => null, | |
| 79 | - 'filter' => null | |
| 80 | - ]; | |
| 81 | - | |
| 82 | - public function __construct(StreamFactory $streamFactory) | |
| 83 | - { | |
| 84 | - $this->streamFactory = $streamFactory; | |
| 85 | - } | |
| 86 | - | |
| 87 | - /** | |
| 88 | - * Sets the part's stream containing the part's headers, content, and | |
| 89 | - * children. | |
| 90 | - * | |
| 91 | - */ | |
| 92 | - public function setStream(StreamInterface $stream) | |
| 93 | - { | |
| 94 | - $this->stream = $stream; | |
| 95 | - } | |
| 96 | - | |
| 97 | - /** | |
| 98 | - * Returns the part's stream containing the part's headers, content, and | |
| 99 | - * children. | |
| 100 | - * | |
| 101 | - * @return StreamInterface | |
| 102 | - */ | |
| 103 | - public function getStream() | |
| 104 | - { | |
| 105 | - // error out if called before setStream, getStream should never return | |
| 106 | - // null. | |
| 107 | - $this->stream->rewind(); | |
| 108 | - return $this->stream; | |
| 109 | - } | |
| 110 | - | |
| 111 | - /** | |
| 112 | - * Returns true if there's a content stream associated with the part. | |
| 113 | - * | |
| 114 | - */ | |
| 115 | - public function hasContent() : bool | |
| 116 | - { | |
| 117 | - return ($this->contentStream !== null); | |
| 118 | - } | |
| 119 | - | |
| 120 | - /** | |
| 121 | - * Attaches the passed stream as the content portion of this | |
| 122 | - * StreamContainer. | |
| 123 | - * | |
| 124 | - * The content stream would represent the content portion of $this->stream. | |
| 125 | - * | |
| 126 | - * If the content is overridden, $this->stream should point to a dynamic | |
| 127 | - * {@see ZBateson\Stream\MessagePartStream} that dynamically creates the | |
| 128 | - * RFC822 formatted message based on the IMessagePart this | |
| 129 | - * PartStreamContainer belongs to. | |
| 130 | - * | |
| 131 | - * setContentStream can be called with 'null' to indicate the IMessagePart | |
| 132 | - * does not contain any content. | |
| 133 | - * | |
| 134 | - * @param StreamInterface $contentStream | |
| 135 | - */ | |
| 136 | - public function setContentStream(?StreamInterface $contentStream = null) | |
| 137 | - { | |
| 138 | - $this->contentStream = $contentStream; | |
| 139 | - $this->decodedStream = null; | |
| 140 | - $this->charsetStream = null; | |
| 141 | - } | |
| 142 | - | |
| 143 | - /** | |
| 144 | - * Returns true if the attached stream filter used for decoding the content | |
| 145 | - * on the current handle is different from the one passed as an argument. | |
| 146 | - * | |
| 147 | - * @param string $transferEncoding | |
| 148 | - */ | |
| 149 | - private function isTransferEncodingFilterChanged(?string $transferEncoding) : bool | |
| 150 | - { | |
| 151 | - return ($transferEncoding !== $this->encoding['type']); | |
| 152 | - } | |
| 153 | - | |
| 154 | - /** | |
| 155 | - * Returns true if the attached stream filter used for charset conversion on | |
| 156 | - * the current handle is different from the one needed based on the passed | |
| 157 | - * arguments. | |
| 158 | - * | |
| 159 | - */ | |
| 160 | - private function isCharsetFilterChanged(string $fromCharset, string $toCharset) : bool | |
| 161 | - { | |
| 162 | - return ($fromCharset !== $this->charset['from'] | |
| 163 | - || $toCharset !== $this->charset['to']); | |
| 164 | - } | |
| 165 | - | |
| 166 | - /** | |
| 167 | - * Attaches a decoding filter to the attached content handle, for the passed | |
| 168 | - * $transferEncoding. | |
| 169 | - * | |
| 170 | - * @param string $transferEncoding | |
| 171 | - */ | |
| 172 | - protected function attachTransferEncodingFilter(?string $transferEncoding) : self | |
| 173 | - { | |
| 174 | - if ($this->decodedStream !== null) { | |
| 175 | - $this->encoding['type'] = $transferEncoding; | |
| 176 | - $assign = null; | |
| 177 | - switch ($transferEncoding) { | |
| 178 | - case 'base64': | |
| 179 | - $assign = $this->streamFactory->newBase64Stream($this->decodedStream); | |
| 180 | - break; | |
| 181 | - case 'x-uuencode': | |
| 182 | - $assign = $this->streamFactory->newUUStream($this->decodedStream); | |
| 183 | - break; | |
| 184 | - case 'quoted-printable': | |
| 185 | - $assign = $this->streamFactory->newQuotedPrintableStream($this->decodedStream); | |
| 186 | - break; | |
| 187 | - } | |
| 188 | - if ($assign !== null) { | |
| 189 | - $this->decodedStream = new CachingStream($assign); | |
| 190 | - } | |
| 191 | - } | |
| 192 | - return $this; | |
| 193 | - } | |
| 194 | - | |
| 195 | - /** | |
| 196 | - * Attaches a charset conversion filter to the attached content handle, for | |
| 197 | - * the passed arguments. | |
| 198 | - * | |
| 199 | - * @param string $fromCharset the character set the content is encoded in | |
| 200 | - * @param string $toCharset the target encoding to return | |
| 201 | - */ | |
| 202 | - protected function attachCharsetFilter(string $fromCharset, string $toCharset) : self | |
| 203 | - { | |
| 204 | - if ($this->charsetStream !== null) { | |
| 205 | - $this->charsetStream = new CachingStream($this->streamFactory->newCharsetStream( | |
| 206 | - $this->charsetStream, | |
| 207 | - $fromCharset, | |
| 208 | - $toCharset | |
| 209 | - )); | |
| 210 | - $this->charset['from'] = $fromCharset; | |
| 211 | - $this->charset['to'] = $toCharset; | |
| 212 | - } | |
| 213 | - return $this; | |
| 214 | - } | |
| 215 | - | |
| 216 | - /** | |
| 217 | - * Resets just the charset stream, and rewinds the decodedStream. | |
| 218 | - */ | |
| 219 | - private function resetCharsetStream() : self | |
| 220 | - { | |
| 221 | - $this->charset = [ | |
| 222 | - 'from' => null, | |
| 223 | - 'to' => null, | |
| 224 | - 'filter' => null | |
| 225 | - ]; | |
| 226 | - $this->decodedStream->rewind(); | |
| 227 | - $this->charsetStream = $this->decodedStream; | |
| 228 | - return $this; | |
| 229 | - } | |
| 230 | - | |
| 231 | - /** | |
| 232 | - * Resets cached encoding and charset streams, and rewinds the stream. | |
| 233 | - */ | |
| 234 | - public function reset() | |
| 235 | - { | |
| 236 | - $this->encoding = [ | |
| 237 | - 'type' => null, | |
| 238 | - 'filter' => null | |
| 239 | - ]; | |
| 240 | - $this->charset = [ | |
| 241 | - 'from' => null, | |
| 242 | - 'to' => null, | |
| 243 | - 'filter' => null | |
| 244 | - ]; | |
| 245 | - $this->contentStream->rewind(); | |
| 246 | - $this->decodedStream = $this->contentStream; | |
| 247 | - $this->charsetStream = $this->contentStream; | |
| 248 | - } | |
| 249 | - | |
| 250 | - /** | |
| 251 | - * Checks what transfer-encoding decoder stream and charset conversion | |
| 252 | - * stream are currently attached on the underlying contentStream, and resets | |
| 253 | - * them if the requested arguments differ from the currently assigned ones. | |
| 254 | - * | |
| 255 | - * @param string $transferEncoding the transfer encoding | |
| 256 | - * @param string $fromCharset the character set the content is encoded in | |
| 257 | - * @param string $toCharset the target encoding to return | |
| 258 | - * @return ?StreamInterface | |
| 259 | - */ | |
| 260 | - public function getContentStream(?string $transferEncoding, ?string $fromCharset, ?string $toCharset) | |
| 261 | - { | |
| 262 | - if ($this->contentStream === null) { | |
| 263 | - return null; | |
| 264 | - } | |
| 265 | - if (empty($fromCharset) || empty($toCharset)) { | |
| 266 | - return $this->getBinaryContentStream($transferEncoding); | |
| 267 | - } | |
| 268 | - if ($this->charsetStream === null | |
| 269 | - || $this->isTransferEncodingFilterChanged($transferEncoding) | |
| 270 | - || $this->isCharsetFilterChanged($fromCharset, $toCharset)) { | |
| 271 | - if ($this->charsetStream === null | |
| 272 | - || $this->isTransferEncodingFilterChanged($transferEncoding)) { | |
| 273 | - $this->reset(); | |
| 274 | - $this->attachTransferEncodingFilter($transferEncoding); | |
| 275 | - } | |
| 276 | - $this->resetCharsetStream(); | |
| 277 | - $this->attachCharsetFilter($fromCharset, $toCharset); | |
| 278 | - } | |
| 279 | - $this->charsetStream->rewind(); | |
| 280 | - return $this->charsetStream; | |
| 281 | - } | |
| 282 | - | |
| 283 | - /** | |
| 284 | - * Checks what transfer-encoding decoder stream is attached on the | |
| 285 | - * underlying stream, and resets it if the requested arguments differ. | |
| 286 | - * | |
| 287 | - * @param string $transferEncoding | |
| 288 | - * @return StreamInterface | |
| 289 | - */ | |
| 290 | - public function getBinaryContentStream(?string $transferEncoding = null) : ?StreamInterface | |
| 291 | - { | |
| 292 | - if ($this->contentStream === null) { | |
| 293 | - return null; | |
| 294 | - } | |
| 295 | - if ($this->decodedStream === null | |
| 296 | - || $this->isTransferEncodingFilterChanged($transferEncoding)) { | |
| 297 | - $this->reset(); | |
| 298 | - $this->attachTransferEncodingFilter($transferEncoding); | |
| 299 | - } | |
| 300 | - $this->decodedStream->rewind(); | |
| 301 | - return $this->decodedStream; | |
| 302 | - } | |
| 303 | -} | |