* SPDX-License-Identifier: AGPL-3.0-or-later */ namespace KTXF\Mail\Entity; /** * Mail Attachment Implementation * * Concrete implementation of IAttachment for mail attachments. * * @since 2025.05.01 */ class Attachment implements IAttachment { /** * @param string $name File name * @param string $mimeType MIME type * @param string $content Binary content * @param string|null $id Attachment ID * @param int|null $size Size in bytes * @param string|null $contentId Content-ID for inline attachments * @param bool $inline Whether inline attachment */ public function __construct( private string $name, private string $mimeType, private string $content, private ?string $id = null, private ?int $size = null, private ?string $contentId = null, private bool $inline = false, ) { if ($this->size === null) { $this->size = strlen($this->content); } } /** * Creates an attachment from a file path * * @since 2025.05.01 * * @param string $path File path * @param string|null $name Override file name * @param string|null $mimeType Override MIME type * * @return self */ public static function fromFile(string $path, ?string $name = null, ?string $mimeType = null): self { $content = file_get_contents($path); $name = $name ?? basename($path); $mimeType = $mimeType ?? mime_content_type($path) ?: 'application/octet-stream'; return new self($name, $mimeType, $content); } /** * Creates an attachment from base64 encoded content * * @since 2025.05.01 * * @param string $name File name * @param string $mimeType MIME type * @param string $base64Content Base64 encoded content * * @return self */ public static function fromBase64(string $name, string $mimeType, string $base64Content): self { return new self($name, $mimeType, base64_decode($base64Content)); } /** * Creates an inline attachment for embedding in HTML * * @since 2025.05.01 * * @param string $name File name * @param string $mimeType MIME type * @param string $content Binary content * @param string $contentId Content-ID (without cid: prefix) * * @return self */ public static function inline(string $name, string $mimeType, string $content, string $contentId): self { return new self($name, $mimeType, $content, null, null, $contentId, true); } /** * Creates from array data * * @since 2025.05.01 * * @param array $data * * @return self */ public static function fromArray(array $data): self { $content = $data['content'] ?? ''; if (isset($data['contentBase64'])) { $content = base64_decode($data['contentBase64']); } return new self( $data[self::JSON_PROPERTY_NAME] ?? $data['name'] ?? '', $data[self::JSON_PROPERTY_MIME_TYPE] ?? $data['mimeType'] ?? 'application/octet-stream', $content, $data[self::JSON_PROPERTY_ID] ?? $data['id'] ?? null, $data[self::JSON_PROPERTY_SIZE] ?? $data['size'] ?? null, $data[self::JSON_PROPERTY_CONTENT_ID] ?? $data['contentId'] ?? null, $data[self::JSON_PROPERTY_INLINE] ?? $data['inline'] ?? false, ); } /** * @inheritDoc */ public function getId(): ?string { return $this->id; } /** * @inheritDoc */ public function getName(): string { return $this->name; } /** * @inheritDoc */ public function getMimeType(): string { return $this->mimeType; } /** * @inheritDoc */ public function getSize(): ?int { return $this->size; } /** * @inheritDoc */ public function getContentId(): ?string { return $this->contentId; } /** * @inheritDoc */ public function isInline(): bool { return $this->inline; } /** * @inheritDoc */ public function getContent(): string { return $this->content; } /** * Gets the content as base64 encoded string * * @since 2025.05.01 * * @return string */ public function getContentBase64(): string { return base64_encode($this->content); } /** * @inheritDoc */ public function jsonSerialize(): array { return array_filter([ self::JSON_PROPERTY_ID => $this->id, self::JSON_PROPERTY_NAME => $this->name, self::JSON_PROPERTY_MIME_TYPE => $this->mimeType, self::JSON_PROPERTY_SIZE => $this->size, self::JSON_PROPERTY_CONTENT_ID => $this->contentId, self::JSON_PROPERTY_INLINE => $this->inline ?: null, 'contentBase64' => $this->getContentBase64(), ], fn($v) => $v !== null); } }