storagePath = $this->rootDir . '/var/cache/mail/composer'; } public function compositionFetch(string $tenantId, string $userId, string $draftId): ?array { $draftDir = $this->draftDir($tenantId, $userId, $draftId); if (!is_dir($draftDir)) { return null; } $messagePath = $draftDir . '/' . self::COMPOSITION_FILENAME; if (!file_exists($messagePath)) { return null; } $decoded = json_decode((string)file_get_contents($messagePath), true); return is_array($decoded) ? $decoded : null; } public function compositionSave(string $tenantId, string $userId, string $draftId, array $snapshot): array { $draftDir = $this->draftDir($tenantId, $userId, $draftId); if (!is_dir($draftDir)) { mkdir($draftDir, 0755, true); } file_put_contents($draftDir . '/' . self::COMPOSITION_FILENAME, json_encode($snapshot, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); return $snapshot; } public function compositionDiscard(string $tenantId, string $userId, string $draftId): bool { $draftDir = $this->draftDir($tenantId, $userId, $draftId); if (!is_dir($draftDir)) { return false; } $this->deleteDir($draftDir); return true; } public function attachmentStageFromStream( string $tenantId, string $userId, string $compositionId, string $attachmentId, BinaryResource $data, ): array { $attachmentDir = $this->attachmentDir($tenantId, $userId, $compositionId); if (!is_dir($attachmentDir)) { mkdir($attachmentDir, 0755, true); } $contentPath = $attachmentDir . '/' . $attachmentId . '.blob'; $handle = fopen($contentPath, 'wb'); $size = 0; foreach ($data->stream() as $chunk) { $chunkString = (string)$chunk; $size += strlen($chunkString); fwrite($handle, $chunkString); } fclose($handle); $meta = [ 'identifier' => $attachmentId, 'composition' => $compositionId, 'name' => $data->filename(), 'type' => $data->mimeType(), 'size' => $size, ]; file_put_contents($attachmentDir . '/' . $attachmentId . '.meta', json_encode($meta, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); return $meta; } public function attachmentStageFromBase64( string $tenantId, string $userId, string $compositionId, string $attachmentId, string $name, string $type, string $data, ): array { $decoded = base64_decode($data, true); if ($decoded === false) { throw new \InvalidArgumentException('Attachment payload is not valid base64'); } $attachmentDir = $this->attachmentDir($tenantId, $userId, $compositionId); if (!is_dir($attachmentDir)) { mkdir($attachmentDir, 0755, true); } file_put_contents($attachmentDir . '/' . $attachmentId . '.blob', $decoded); $metadata = [ 'identifier' => $attachmentId, 'composition' => $compositionId, 'name' => $name, 'type' => $type, 'size' => strlen($decoded), ]; file_put_contents($attachmentDir . '/' . $attachmentId . '.meta', json_encode($metadata, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); return $metadata; } public function attachmentFetchData(string $tenantId, string $userId, string $compositionId, string $attachmentId): ?string { $path = $this->attachmentDir($tenantId, $userId, $compositionId) . '/' . $attachmentId . '.blob'; if (!file_exists($path)) { return null; } return file_get_contents($path) ?: null; } public function fetchAttachmentMeta(string $tenantId, string $userId, string $compositionId, string $attachmentId): ?array { $path = $this->attachmentDir($tenantId, $userId, $compositionId) . '/' . $attachmentId . '.meta'; if (!file_exists($path)) { return null; } $decoded = json_decode((string)file_get_contents($path), true); return is_array($decoded) ? $decoded : null; } private function attachmentDir(string $tenantId, string $userId, string $draftId): string { return $this->draftDir($tenantId, $userId, $draftId) . '/attachments'; } private function draftDir(string $tenantId, string $userId, string $draftId): string { return $this->storagePath . '/' . $tenantId . '/' . $userId . '/' . $draftId; } private function deleteDir(string $path): void { if (!is_dir($path)) { return; } foreach (scandir($path) ?: [] as $entry) { if ($entry === '.' || $entry === '..') { continue; } $child = $path . '/' . $entry; if (is_dir($child)) { $this->deleteDir($child); continue; } unlink($child); } rmdir($path); } }