RapportZipAnalyzer

Version identique à db_rapport.php

mais transformé en Programmation orienté objet

<?php
/**
 * RapportZipAnalyzer.php
 * Analyse un fichier ZIP contenant un export NDJSON de SMS/MMS
 */

class RapportZipAnalyzer
{
    private string $zip_name;
    private string $zip_path;
    private ZipArchive $zip;

    private int    $nb_sms         = 0;
    private int    $nb_mms         = 0;
    private int    $nb_pj          = 0;
    private int    $nb_pj_absentes = 0;
    private array  $pj_absentes    = [];
    private int    $date_min;
    private int    $date_max       = 0;
    private array  $err_not_json   = [];

    public function __construct(string $zip_name)
    {
        $this->zip_name = $zip_name;
        $this->date_min = PHP_INT_MAX;
    }

    // =========================================================
    // API publique
    // =========================================================

    public function analyser(): array
    {
        try {
            $this->resoudreCheminZip();
            $this->ouvrirZip();
            $lignes = $this->lireNdjson();
            foreach ($lignes as $k => $line) {
                $this->traiterLigne($k, $line);
            }
            $this->zip->close();
        } catch (\RuntimeException $e) {
            if (isset($this->zip)) $this->zip->close();
            return ['erreur' => $e->getMessage()];
        }

        return $this->buildRapport();
    }

    // =========================================================
    // Étapes internes
    // =========================================================

    private function resoudreCheminZip(): void
    {
        if (empty($this->zip_name)) {
            throw new \RuntimeException('Nom de fichier ZIP vide.');
        }
        $base = rtrim($_ENV['BKUP_MESSAGES_PATH'] ?? dirname(__DIR__) . '/bkup_messages', '/');
        $path = $base . '/' . basename($this->zip_name);
        if (!file_exists($path)) {
            throw new \RuntimeException('Fichier ZIP introuvable.');
        }
        $this->zip_path = $path;
    }

    private function ouvrirZip(): void
    {
        $this->zip = new ZipArchive();
        if ($this->zip->open($this->zip_path) !== true) {
            throw new \RuntimeException('Impossible d\'ouvrir le ZIP.');
        }
    }

    private function lireNdjson(): array
    {
        if ($this->zip->locateName('messages.ndjson') === false) {
            throw new \RuntimeException('messages.ndjson introuvable dans l\'archive.');
        }
        $contenu = $this->zip->getFromName('messages.ndjson');
        if ($contenu === false) {
            throw new \RuntimeException('Impossible de lire messages.ndjson dans l\'archive.');
        }
        return explode("\n", trim($contenu));
    }

    private function traiterLigne(int $k, string $line): void
    {
        $json = json_decode($line, true);

        if (json_last_error() !== JSON_ERROR_NONE || !is_array($json)) {
            $this->err_not_json[] = ['l' => $k, 'c' => $line];
            return;
        }
        if (!isset($json['date'])) {
            $this->err_not_json[] = ['l' => $k, 'c' => $line, 'raison' => 'date manquante'];
            return;
        }

        if ($this->isSms($json)) {
            $this->nb_sms++;
            $date = intdiv((int) $json['date'], 1000);
        } else {
            $this->nb_mms++;
            $date = (int) $json['date'];
            $this->traiterPiecesJointes($json);
        }

        $this->majDates($date);
    }

    private function isSms(array $json): bool
    {
        return !isset($json['__sender_address']) && !isset($json['__recipient_addresses']);
    }

    private function traiterPiecesJointes(array $json): void
    {
        if (!isset($json['__parts'])) return;

        foreach ($json['__parts'] as $part) {
            if (!isset($part['_data'])) continue;
            $this->nb_pj++;
            $nom_fichier = 'data/' . basename($part['_data']);
            if ($this->zip->locateName($nom_fichier) === false) {
                $this->nb_pj_absentes++;
                $this->pj_absentes[] = $nom_fichier;
            }
        }
    }

    private function majDates(int $date): void
    {
        if ($date < $this->date_min) $this->date_min = $date;
        if ($date > $this->date_max) $this->date_max = $date;
    }

    private function buildRapport(): array
    {
        $total = $this->nb_sms + $this->nb_mms;
        return [
            'nb_sms'         => $this->nb_sms,
            'nb_mms'         => $this->nb_mms,
            'nb_pj'          => $this->nb_pj,
            'nb_pj_absentes' => $this->nb_pj_absentes,
            'pj_absentes'    => $this->pj_absentes,
            'date_debut'     => $total > 0 ? date('Y-m-d H:i:s', $this->date_min) : null,
            'date_fin'       => $total > 0 ? date('Y-m-d H:i:s', $this->date_max) : null,
            'err_not_json'   => $this->err_not_json,
        ];
    }
}