parse()->getAllAttachmentParts() as $part) { if (static::isForwardedMessage($part)) { $attachments = array_merge($attachments, (new FileMessage($part->getContent()))->attachments()); } else { $attachments[] = new Attachment( $part->getFilename(), $part->getContentId(), $part->getContentType(), $part->getContentDisposition(), $part->getBinaryContentStream() ?? Utils::streamFor(''), ); } } return $attachments; } /** * Get attachments from a message's body structure using lazy streams. * * @return Attachment[] */ public static function lazy(Message $message): array { $attachments = []; foreach ($message->bodyStructure(fetch: true)?->attachments() ?? [] as $part) { $attachments[] = new Attachment( $part->filename(), $part->id(), $part->contentType(), $part->disposition()?->type()?->value, new Support\LazyBodyPartStream($message, $part), ); } return $attachments; } /** * Determine if the attachment should be treated as an embedded forwarded message. */ protected static function isForwardedMessage(IMessagePart $part): bool { return empty($part->getFilename()) && strtolower((string) $part->getContentType()) === 'message/rfc822' && strtolower((string) $part->getContentDisposition()) !== 'attachment'; } /** * Get the attachment's filename. */ public function filename(): ?string { return $this->filename; } /** * Get the attachment's content ID. */ public function contentId(): ?string { return $this->contentId; } /** * Get the attachment's content type. */ public function contentType(): string { return $this->contentType; } /** * Get the attachment's content disposition. */ public function contentDisposition(): string { return $this->contentDisposition; } /** * Get the attachment's contents. */ public function contents(): string { if ($this->contentStream->isSeekable()) { $this->contentStream->rewind(); } return $this->contentStream->getContents(); } /** * Get the attachment's content stream. */ public function contentStream(): StreamInterface { return $this->contentStream; } /** * Save the attachment to a file. */ public function save(string $path): false|int { return file_put_contents($path, $this->contents()); } /** * Get the attachment's extension. */ public function extension(): ?string { if ($ext = pathinfo($this->filename ?? '', PATHINFO_EXTENSION)) { return $ext; } if ($ext = (MimeTypes::getDefault()->getExtensions($this->contentType)[0] ?? null)) { return $ext; } return null; } /** * Get the array representation of the attachment. */ public function toArray(): array { return [ 'filename' => $this->filename, 'content_type' => $this->contentType, 'contents' => $this->contents(), ]; } /** * Get the JSON representation of the attachment. */ public function jsonSerialize(): array { return $this->toArray(); } }