Files
itflow/libs/vendor/directorytree/imapengine/src/Attachment.php
2026-07-10 18:37:16 -04:00

179 lines
4.4 KiB
PHP

<?php
namespace DirectoryTree\ImapEngine;
use GuzzleHttp\Psr7\Utils;
use Illuminate\Contracts\Support\Arrayable;
use JsonSerializable;
use Psr\Http\Message\StreamInterface;
use Symfony\Component\Mime\MimeTypes;
use ZBateson\MailMimeParser\Message\IMessagePart;
class Attachment implements Arrayable, JsonSerializable
{
/**
* Constructor.
*/
public function __construct(
protected ?string $filename,
protected ?string $contentId,
protected string $contentType,
protected ?string $contentDisposition,
protected StreamInterface $contentStream,
) {}
/**
* Get attachments from a parsed message.
*
* @return Attachment[]
*/
public static function parsed(MessageInterface $message): array
{
$attachments = [];
foreach ($message->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();
}
}