>}> */ private array $sheets = []; /** * Tambah satu worksheet. Baris pertama dianggap pengepala (huruf tebal). * * @param array> $rows */ public function addSheet(string $name, array $rows): static { $this->sheets[] = ['name' => $this->namaSah($name), 'rows' => $rows]; return $this; } /** * Hasilkan kandungan binari fail .xlsx. */ public function toString(): string { $tmp = tempnam(sys_get_temp_dir(), 'xlsx'); $zip = new ZipArchive; $zip->open($tmp, ZipArchive::OVERWRITE); $zip->addFromString('[Content_Types].xml', $this->contentTypes()); $zip->addFromString('_rels/.rels', $this->rootRels()); $zip->addFromString('xl/workbook.xml', $this->workbook()); $zip->addFromString('xl/_rels/workbook.xml.rels', $this->workbookRels()); $zip->addFromString('xl/styles.xml', $this->styles()); foreach ($this->sheets as $i => $sheet) { $zip->addFromString('xl/worksheets/sheet'.($i + 1).'.xml', $this->sheetXml($sheet['rows'])); } $zip->close(); $data = file_get_contents($tmp); @unlink($tmp); return $data; } private function namaSah(string $name): string { $name = preg_replace('/[\\\\\/\*\?\[\]:]/', ' ', $name); return mb_substr(trim($name), 0, 31) ?: 'Sheet'; } /** * Tukar indeks lajur (0-asas) kepada huruf A, B, … AA. */ private function col(int $index): string { $letters = ''; $index++; while ($index > 0) { $mod = ($index - 1) % 26; $letters = chr(65 + $mod).$letters; $index = intdiv($index - 1, 26); } return $letters; } private function cell(string $ref, string $value, int $style): string { $v = htmlspecialchars($value, ENT_QUOTES | ENT_XML1, 'UTF-8'); $s = $style > 0 ? ' s="'.$style.'"' : ''; return ''.$v.''; } /** * @param array> $rows */ private function sheetXml(array $rows): string { $xml = '' .''; foreach ($rows as $r => $cells) { $rowNum = $r + 1; $style = $r === 0 ? 1 : 0; $xml .= ''; foreach (array_values($cells) as $c => $val) { $xml .= $this->cell($this->col($c).$rowNum, (string) $val, $style); } $xml .= ''; } return $xml.''; } private function contentTypes(): string { $overrides = ''; foreach ($this->sheets as $i => $s) { $overrides .= ''; } return '' .'' .'' .'' .'' .'' .$overrides .''; } private function rootRels(): string { return '' .'' .'' .''; } private function workbook(): string { $sheetsXml = ''; foreach ($this->sheets as $i => $s) { $name = htmlspecialchars($s['name'], ENT_QUOTES | ENT_XML1, 'UTF-8'); $sheetsXml .= ''; } return '' .'' .''.$sheetsXml.''; } private function workbookRels(): string { $rels = ''; $count = count($this->sheets); foreach ($this->sheets as $i => $s) { $rels .= ''; } $rels .= ''; return '' .'' .$rels.''; } private function styles(): string { return '' .'' .'' .'' .'' .'' .'' .'' .'' .'' .'' .'' .'' .''; } }