getActiveSheet(); $sheet->setTitle($this->sanitizeSheetTitle($sheetTitle)); // Ligne 1 : en-tete. $sheet->fromArray($headers, null, 'A1'); // Lignes 2..n : donnees. Iteration manuelle pour supporter un iterable // paresseux (generator) sans tout materialiser en memoire. $rowNumber = 2; foreach ($rows as $row) { $sheet->fromArray($row, null, 'A'.$rowNumber); ++$rowNumber; } return $this->toBinary($spreadsheet); } private function toBinary(Spreadsheet $spreadsheet): string { $writer = new Xlsx($spreadsheet); // Le writer ecrit vers un chemin de fichier : on passe par un fichier // temporaire puis on lit son contenu binaire. $tmpFile = tempnam(sys_get_temp_dir(), 'xlsx_export_'); if (false === $tmpFile) { throw new RuntimeException('Impossible de creer un fichier temporaire pour l\'export XLSX.'); } try { $writer->save($tmpFile); $binary = file_get_contents($tmpFile); if (false === $binary) { throw new RuntimeException('Lecture du fichier XLSX temporaire impossible.'); } return $binary; } finally { // Libere les references internes de PhpSpreadsheet puis supprime le // fichier temporaire, meme en cas d'exception. $spreadsheet->disconnectWorksheets(); @unlink($tmpFile); } } /** * Retire les caracteres interdits et tronque a 31 caracteres ; renvoie un * titre par defaut si la chaine resultante est vide. */ private function sanitizeSheetTitle(string $title): string { $clean = str_replace(str_split(self::INVALID_TITLE_CHARS), '', $title); $clean = mb_substr($clean, 0, self::MAX_SHEET_TITLE_LENGTH); return '' === $clean ? 'Export' : $clean; } }