Files
provider_imap/lib/Client/MessagePart.php
2026-05-23 20:18:58 -04:00

218 lines
5.7 KiB
PHP

<?php
declare(strict_types=1);
namespace KTXM\ProviderImap\Client;
final class MessagePart
{
/**
* @param array<string, string> $parameters
* @param array<string, string> $dispositionParameters
* @param list<string> $language
* @param list<MessagePart> $parts
*/
public function __construct(
private readonly string $partId,
private readonly string $mimeType,
private readonly array $parameters = [],
private readonly ?string $contentId = null,
private readonly ?string $description = null,
private readonly ?string $encoding = null,
private readonly ?int $size = null,
private readonly ?string $disposition = null,
private readonly array $dispositionParameters = [],
private readonly array $language = [],
private readonly ?string $location = null,
private readonly ?string $content = null,
private readonly array $parts = [],
) {}
public function partId(): string
{
return $this->partId;
}
public function mimeType(): string
{
return $this->mimeType;
}
/**
* @return array<string, string>
*/
public function parameters(): array
{
return $this->parameters;
}
public function contentId(): ?string
{
return $this->contentId;
}
public function description(): ?string
{
return $this->description;
}
public function encoding(): ?string
{
return $this->encoding;
}
public function size(): ?int
{
return $this->size;
}
public function disposition(): ?string
{
return $this->disposition;
}
/**
* @return array<string, string>
*/
public function dispositionParameters(): array
{
return $this->dispositionParameters;
}
/**
* @return list<string>
*/
public function language(): array
{
return $this->language;
}
public function location(): ?string
{
return $this->location;
}
public function content(): ?string
{
return $this->content;
}
/**
* @return list<MessagePart>
*/
public function parts(): array
{
return $this->parts;
}
public function isMultipart(): bool
{
return str_starts_with($this->mimeType, 'multipart/');
}
/**
* @param array<string, string> $sections
*/
public function withInjectedSections(array $sections): self
{
if ($this->parts !== []) {
$parts = [];
foreach ($this->parts as $part) {
$parts[] = $part->withInjectedSections($sections);
}
return new self(
$this->partId,
$this->mimeType,
$this->parameters,
$this->contentId,
$this->description,
$this->encoding,
$this->size,
$this->disposition,
$this->dispositionParameters,
$this->language,
$this->location,
$this->content,
$parts,
);
}
if (!str_starts_with($this->mimeType, 'text/')) {
return $this;
}
if (!array_key_exists($this->partId, $sections)) {
return $this;
}
return new self(
$this->partId,
$this->mimeType,
$this->parameters,
$this->contentId,
$this->description,
$this->encoding,
$this->size,
$this->disposition,
$this->dispositionParameters,
$this->language,
$this->location,
self::decodeContent($sections[$this->partId], $this->encoding, $this->parameters['charset'] ?? 'us-ascii'),
[],
);
}
public function toArray(): array
{
$data = [
'partId' => $this->partId,
'blobId' => $this->partId,
'cId' => $this->contentId,
'type' => $this->mimeType,
'charset' => $this->parameters['charset'] ?? null,
'name' => $this->parameters['name'] ?? $this->dispositionParameters['filename'] ?? null,
'encoding' => $this->encoding,
'size' => $this->size,
'disposition' => $this->disposition,
'language' => $this->language === [] ? null : implode(',', $this->language),
'location' => $this->location,
'content' => $this->content,
];
$children = [];
foreach ($this->parts as $part) {
$children[] = $part->toArray();
}
$data['subParts'] = $children === [] ? null : $children;
return array_filter($data, static fn (mixed $value): bool => $value !== null);
}
private static function decodeContent(string $content, ?string $encoding, string $charset): string
{
$decoded = match (strtolower($encoding ?? '7bit')) {
'quoted-printable' => quoted_printable_decode($content),
'base64' => base64_decode($content, true) ?: '',
default => $content,
};
if ($charset === '' || in_array(strtolower($charset), ['utf-8', 'utf8'], true)) {
return mb_convert_encoding($decoded, 'UTF-8', 'UTF-8');
}
try {
$converted = mb_convert_encoding($decoded, 'UTF-8', $charset);
if ($converted !== false) {
return $converted;
}
} catch (\ValueError) {
}
$converted = @iconv($charset, 'UTF-8//TRANSLIT//IGNORE', $decoded);
$decoded = $converted !== false ? $converted : $decoded;
return mb_convert_encoding($decoded, 'UTF-8', 'UTF-8');
}
}