← Code-Übersicht

DateBasedInvoiceStorage.php

Pfad: src/Infrastructure/Rechnung/DateBasedInvoiceStorage.php

Ext: php

Größe: 2915 Bytes

Geändert: 2026-07-16T14:11:10+02:00

Frühere Version vom 2026-07-16T14:11:10+02:00 · zur aktuellen Fassung

<?php
declare(strict_types=1);

namespace Demo\Infrastructure\Rechnung;

use Demo\Domain\Rechnung\InvoiceFileStoragePort;

/**
 * Erzeugt lokale Pfade YYYY/MM/YYYY-MM-DD_<firma>_<hash>.pdf und speichert die PDF.
 */
final class DateBasedInvoiceStorage implements InvoiceFileStoragePort
{
    /**
     * @param string $basePath Stammpfad (z. B. /var/www/.../rechnungen/originale).
     */
    public function __construct(
        private string $basePath
    ) {
    }

    /**
     * @inheritDoc
     */
    public function buildTargetPath(?string $invoiceDateHint, string $vendorName, string $sourceId): string
    {
        $date = $this->normalizeDate($invoiceDateHint);
        $dir = $this->basePath . '/' . $date->format('Y') . '/' . $date->format('m');

        if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
            throw new \RuntimeException('Ablageverzeichnis fuer PDF kann nicht erzeugt werden');
        }

        $safeVendor = $this->sanitize($vendorName);
        $shortId = substr(hash('sha1', $sourceId), 0, 8);
        $filename = $date->format('Y-m-d') . '_' . $safeVendor . '_' . $shortId . '.pdf';

        return $dir . '/' . $filename;
    }

    /**
     * @inheritDoc
     */
    public function persistDownloadedPdf(string $pdfBinary, string $targetPath): void
    {
        if (is_file($targetPath)) {
            return;
        }

        if (file_put_contents($targetPath, $pdfBinary, LOCK_EX) === false) {
            throw new \RuntimeException('Konnte PDF lokal nicht speichern: ' . $targetPath);
        }
    }

    /**
     * @inheritDoc
     */
    public function sha256(string $path): string
    {
        $hash = hash_file('sha256', $path);
        return $hash === false ? '' : $hash;
    }

    /**
     * @param string $input Eingabe.
     * @return string Bereinigter Dateiname.
     */
    private function sanitize(string $input): string
    {
        $reduced = strtolower(trim($input));
        if ($reduced === '') {
            return 'unbekannt';
        }

        $reduced = preg_replace('/\s+/', '_', $reduced);
        $reduced = preg_replace('/[^a-z0-9_-]/i', '', $reduced);

        return trim((string) $reduced, '_') ?: 'unbekannt';
    }

    /**
     * @param string|null $invoiceDateHint Datumstext.
     * @return \DateTimeImmutable Normalisiertes Datum.
     */
    private function normalizeDate(?string $invoiceDateHint): \DateTimeImmutable
    {
        $formats = ['Y-m-d', 'd.m.Y', 'd/m/Y', 'Y/m/d'];
        if ($invoiceDateHint !== null) {
            $invoiceDateHint = trim($invoiceDateHint);
            foreach ($formats as $format) {
                $date = \DateTimeImmutable::createFromFormat($format, $invoiceDateHint);
                if ($date instanceof \DateTimeImmutable) {
                    return $date;
                }
            }
        }

        return new \DateTimeImmutable('today');
    }
}