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,
];
}
}
