Certified/app/Services/CertificateZipper.php
Joël van de Wouw ac340d125c
Some checks failed
linter / quality (push) Failing after 13s
tests / ci (8.3) (push) Failing after 2s
tests / ci (8.4) (push) Failing after 2s
tests / ci (8.5) (push) Failing after 3s
Initial commit
Certificate manager built on Laravel 13, Livewire 4, and Flux.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-12 14:44:58 +02:00

74 lines
2.2 KiB
PHP

<?php
namespace App\Services;
use App\Models\Certificate;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use RuntimeException;
use ZipArchive;
class CertificateZipper
{
/**
* Build a password-protected (AES-256) ZIP archive containing the given
* certificates' files. Returns the absolute path to the created archive;
* the caller is responsible for deleting it once sent.
*
* @param Collection<int, Certificate> $certificates
*/
public function create(Collection $certificates, string $password): string
{
$archivePath = tempnam(sys_get_temp_dir(), 'certs_').'.zip';
$zip = new ZipArchive;
if ($zip->open($archivePath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
throw new RuntimeException('Unable to create ZIP archive.');
}
$zip->setPassword($password);
$usedNames = [];
foreach ($certificates as $certificate) {
if (blank($certificate->file_path) || ! Storage::disk('local')->exists($certificate->file_path)) {
continue;
}
$entryName = $this->uniqueEntryName($certificate, $usedNames);
$usedNames[] = $entryName;
$zip->addFromString($entryName, Storage::disk('local')->get($certificate->file_path));
$zip->setEncryptionName($entryName, ZipArchive::EM_AES_256);
}
if ($zip->count() === 0) {
$zip->close();
@unlink($archivePath);
throw new RuntimeException('No files were available to add to the archive.');
}
$zip->close();
return $archivePath;
}
/**
* @param array<int, string> $usedNames
*/
private function uniqueEntryName(Certificate $certificate, array $usedNames): string
{
$extension = pathinfo($certificate->file_path, PATHINFO_EXTENSION);
$base = Str::slug($certificate->title) ?: 'certificate';
$name = $base.'.'.$extension;
$counter = 1;
while (in_array($name, $usedNames, true)) {
$name = $base.'-'.(++$counter).'.'.$extension;
}
return $name;
}
}