1 | <?php |
||
2 | |||
3 | /** |
||
4 | * Class Builder. |
||
5 | */ |
||
6 | |||
7 | namespace Zhaqq\Xlsx\Writer; |
||
8 | |||
9 | use Zhaqq\Xlsx\Support; |
||
10 | use Zhaqq\Exception\XlsxException; |
||
11 | |||
12 | /** |
||
13 | * Class Builder. |
||
14 | */ |
||
15 | class Builder |
||
16 | { |
||
17 | use Style; |
||
18 | |||
19 | const EXCEL_2007_MAX_ROW = 1048576; |
||
20 | |||
21 | const EXCEL_2007_MAX_COL = 16384; |
||
22 | |||
23 | /** |
||
24 | * @var Sheet[] |
||
25 | */ |
||
26 | protected $sheets = []; |
||
27 | |||
28 | /** |
||
29 | * @var Sheet |
||
30 | */ |
||
31 | protected $sheet; |
||
32 | |||
33 | /** |
||
34 | * @var |
||
35 | */ |
||
36 | protected $sheetName; |
||
37 | |||
38 | /** |
||
39 | * @var string |
||
40 | */ |
||
41 | protected $sheetWriter = 'Xlsx'; |
||
42 | |||
43 | /** |
||
44 | * 缓存文件列表. |
||
45 | * |
||
46 | * @var array |
||
47 | */ |
||
48 | protected $tempFiles = []; |
||
49 | |||
50 | /** |
||
51 | * @var |
||
52 | */ |
||
53 | protected $title = ''; |
||
54 | |||
55 | /** |
||
56 | * @var |
||
57 | */ |
||
58 | protected $subject = ''; |
||
59 | |||
60 | /** |
||
61 | * @var |
||
62 | */ |
||
63 | protected $author = ''; |
||
64 | |||
65 | /** |
||
66 | * @var |
||
67 | */ |
||
68 | protected $company = ''; |
||
69 | |||
70 | /** |
||
71 | * @var |
||
72 | */ |
||
73 | protected $description = ''; |
||
74 | |||
75 | /** |
||
76 | * @var array |
||
77 | */ |
||
78 | protected $keywords = []; |
||
79 | |||
80 | protected $tempDir; |
||
81 | |||
82 | /** |
||
83 | * XlsxWriter constructor. |
||
84 | */ |
||
85 | public function __construct() |
||
86 | { |
||
87 | if (!ini_get('date.timezone')) { |
||
88 | date_default_timezone_set('PRC'); |
||
89 | } |
||
90 | $this->addCellStyle($numberFormat = 'GENERAL', $styleString = null); |
||
91 | $this->addCellStyle($numberFormat = 'GENERAL', $styleString = null); |
||
92 | $this->addCellStyle($numberFormat = 'GENERAL', $styleString = null); |
||
93 | $this->addCellStyle($numberFormat = 'GENERAL', $styleString = null); |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * 写入sheet 不带header头 需先执行 $this->writeSheetHeader 初始化头部. |
||
98 | * |
||
99 | * @param \Generator|array $rows |
||
100 | * |
||
101 | * @example $rows = [ |
||
102 | * 'sheet_name' => 'sheet_name_1', |
||
103 | * 'row' => [ |
||
104 | * 'title' => 'title1', |
||
105 | * 'content' => 'content1' |
||
106 | * ] |
||
107 | * ]; |
||
108 | */ |
||
109 | public function addSheetRows($rows) |
||
110 | { |
||
111 | foreach ($rows as $row) { |
||
112 | !isset($row['options']) && $row['options'] = null; |
||
113 | $this->writeSheetRow($row['sheet_name'], $row['row'], $row['options']); |
||
114 | } |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * 写入sheet 带header头 如headers为空需先执行 $this->writeSheetHeader 初始化头部. |
||
119 | * |
||
120 | * @param \Generator|array $rows |
||
121 | * @param array $headers |
||
122 | * |
||
123 | * @example $rows = [ |
||
124 | * 'sheet_name' => 'sheet_name_1', |
||
125 | * 'row' => [ |
||
126 | * 'title' => 'title1', |
||
127 | * 'content' => 'content1' |
||
128 | * ] |
||
129 | * ]; |
||
130 | * $headers => [ |
||
131 | * 'sheet_name' => 'sheet_name_1', |
||
132 | * 'types' => [ |
||
133 | * 'title' => 'string', // 标明类型 |
||
134 | * 'content' => 'string' |
||
135 | * ] |
||
136 | * |
||
137 | * ]; |
||
138 | */ |
||
139 | public function addSheetRowsWithHeaders($rows, $headers = []) |
||
140 | { |
||
141 | if (!empty($headers) && is_array($headers)) { |
||
142 | foreach ($headers as $header) { |
||
143 | !isset($header['options']) && $header['options'] = null; |
||
144 | $this->buildHeader($header['sheet_name'], $header['types'], $header['options'] = null); |
||
145 | } |
||
146 | } |
||
147 | foreach ($rows as $row) { |
||
148 | !isset($row['options']) && $row['options'] = null; |
||
149 | $this->writeSheetRow($row['sheet_name'], $row['row'], $row['options']); |
||
150 | } |
||
151 | } |
||
152 | |||
153 | /** |
||
154 | * 写入文件. |
||
155 | * |
||
156 | * @param $filename |
||
157 | */ |
||
158 | public function writeToFile($filename) |
||
159 | { |
||
160 | foreach ($this->sheets as $sheetName => $sheet) { |
||
161 | $this->finalizeSheet($sheetName); //making sure all footers have been written |
||
162 | } |
||
163 | |||
164 | if (file_exists($filename)) { |
||
165 | if (is_writable($filename)) { |
||
166 | @unlink($filename); //if the zip already exists, remove it |
||
0 ignored issues
–
show
|
|||
167 | } else { |
||
168 | throw new XlsxException('Error in '.__CLASS__.'::'.__FUNCTION__.', file is not writeable.'); |
||
169 | } |
||
170 | } |
||
171 | $zip = new \ZipArchive(); |
||
172 | if (empty($this->sheets)) { |
||
173 | throw new XlsxException('Error in '.__CLASS__.'::'.__FUNCTION__.', no worksheets defined.'); |
||
174 | } |
||
175 | if (!$zip->open($filename, \ZipArchive::CREATE)) { |
||
176 | throw new XlsxException('Error in '.__CLASS__.'::'.__FUNCTION__.', unable to create zip.'); |
||
177 | } |
||
178 | $zip->addEmptyDir('docProps/'); |
||
179 | $zip->addFromString('docProps/app.xml', XlsxBuilder::buildAppXML($this->company)); |
||
180 | $zip->addFromString('docProps/core.xml', XlsxBuilder::buildCoreXML( |
||
181 | $this->title, |
||
182 | $this->subject, |
||
183 | $this->author, |
||
184 | $this->keywords, |
||
185 | $this->description |
||
186 | )); |
||
187 | $zip->addEmptyDir('_rels/'); |
||
188 | $zip->addFromString('_rels/.rels', XlsxBuilder::buildRelationshipsXML()); |
||
189 | $zip->addEmptyDir('xl/worksheets/'); |
||
190 | foreach ($this->sheets as $sheet) { |
||
191 | $zip->addFile($sheet->filename, 'xl/worksheets/'.$sheet->xmlname); |
||
192 | } |
||
193 | $zip->addFromString('xl/workbook.xml', XlsxBuilder::buildWorkbookXML($this->sheets)); |
||
194 | $zip->addFile($this->writeStylesXML(), 'xl/styles.xml'); //$zip->addFromString("xl/styles.xml", self::buildStylesXML() ); |
||
195 | $zip->addFromString('[Content_Types].xml', XlsxBuilder::buildContentTypesXML($this->sheets)); |
||
196 | $zip->addEmptyDir('xl/_rels/'); |
||
197 | $zip->addFromString('xl/_rels/workbook.xml.rels', XlsxBuilder::buildWorkbookRelsXML($this->sheets)); |
||
198 | |||
199 | $zip->close(); |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * @param $sheetName |
||
204 | * @param array $row |
||
205 | * @param array $rowOptions |
||
206 | */ |
||
207 | public function writeSheetRow($sheetName, array $row, $rowOptions = []) |
||
208 | { |
||
209 | if (empty($sheetName)) { |
||
210 | return; |
||
211 | } |
||
212 | $this->initSheet($sheetName); |
||
213 | $sheet = $this->getSheet($sheetName); |
||
214 | if (count($sheet->columns) < count($row)) { |
||
215 | $defaultColumnTypes = $this->initColumnsTypes(array_fill($from = 0, count($row), 'GENERAL')); //will map to n_auto |
||
216 | $sheet->columns = array_merge((array) $sheet->columns, $defaultColumnTypes); |
||
217 | } |
||
218 | |||
219 | if (!empty($rowOptions)) { |
||
220 | $ht = isset($rowOptions['height']) ? floatval($rowOptions['height']) : 12.1; |
||
221 | $customHt = isset($rowOptions['height']) ? true : false; |
||
222 | $hidden = isset($rowOptions['hidden']) ? (bool) ($rowOptions['hidden']) : false; |
||
223 | $collapsed = isset($rowOptions['collapsed']) ? (bool) ($rowOptions['collapsed']) : false; |
||
224 | $sheet->fileWriter->write( |
||
225 | '<row collapsed="'.($collapsed).'" customFormat="false" customHeight="'.($customHt). |
||
226 | '" hidden="'.($hidden).'" ht="'.($ht).'" outlineLevel="0" r="'.($sheet->rowCount + 1).'">' |
||
227 | ); |
||
228 | } else { |
||
229 | $sheet->fileWriter->write( |
||
230 | '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'. |
||
231 | ($sheet->rowCount + 1).'">' |
||
232 | ); |
||
233 | } |
||
234 | |||
235 | $style = $rowOptions; |
||
236 | $c = 0; |
||
237 | foreach ($row as $v) { |
||
238 | $numberFormat = $sheet->columns[$c]['number_format']; |
||
239 | $numberFormatType = $sheet->columns[$c]['number_format_type']; |
||
240 | $cellStyleIdx = empty($style) ? $sheet->columns[$c]['default_cell_style'] : |
||
241 | $this->addCellStyle($numberFormat, json_encode(isset($style[0]) ? $style[$c] : $style)); |
||
242 | $sheet->writeCell($sheet->rowCount, $c, $v, $numberFormatType, $cellStyleIdx); |
||
243 | ++$c; |
||
244 | } |
||
245 | $sheet->fileWriter->write('</row>'); |
||
246 | ++$sheet->rowCount; |
||
247 | |||
248 | $this->sheetName = $sheetName; |
||
249 | } |
||
250 | |||
251 | /** |
||
252 | * @param $sheetName |
||
253 | */ |
||
254 | protected function finalizeSheet($sheetName) |
||
255 | { |
||
256 | if (empty($sheetName) || $this->sheets[$sheetName]->finalized) { |
||
257 | return; |
||
258 | } |
||
259 | |||
260 | $sheet = $this->sheets[$sheetName]; |
||
261 | $sheet->finallyContent(); |
||
262 | } |
||
263 | |||
264 | /** |
||
265 | * @return bool|string |
||
266 | */ |
||
267 | protected function writeStylesXML() |
||
268 | { |
||
269 | $r = $this->styleFontIndexes($this->cellStyles); |
||
270 | $fills = $r['fills']; |
||
271 | $fonts = $r['fonts']; |
||
272 | $borders = $r['borders']; |
||
273 | $styleIndexes = $r['styles']; |
||
274 | |||
275 | $temporaryFilename = $this->tempFilename(); |
||
276 | $file = new XlsxWriterBuffer($temporaryFilename); |
||
277 | $file->write('<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'."\n"); |
||
278 | $file->write('<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">'); |
||
279 | $file->write('<numFmts count="'.count($this->numberFormats).'">'); |
||
280 | foreach ($this->numberFormats as $i => $v) { |
||
281 | $file->write('<numFmt numFmtId="'.(164 + $i).'" formatCode="'.Support::xmlSpecialChars($v).'" />'); |
||
282 | } |
||
283 | $file->write('</numFmts>'); |
||
284 | $file->write('<fonts count="'.(count($fonts)).'">'); |
||
285 | $file->write('<font><name val="Arial"/><charset val="1"/><family val="2"/><sz val="10"/></font>'); |
||
286 | $file->write('<font><name val="Arial"/><family val="0"/><sz val="10"/></font>'); |
||
287 | $file->write('<font><name val="Arial"/><family val="0"/><sz val="10"/></font>'); |
||
288 | $file->write('<font><name val="Arial"/><family val="0"/><sz val="10"/></font>'); |
||
289 | |||
290 | foreach ($fonts as $font) { |
||
291 | if (!empty($font)) { //fonts have 4 empty placeholders in array to offset the 4 static xml entries above |
||
292 | $f = json_decode($font, true); |
||
293 | $file->write('<font>'); |
||
294 | $file->write('<name val="'.htmlspecialchars($f['name']).'"/><charset val="1"/><family val="'.intval($f['family']).'"/>'); |
||
295 | $file->write('<sz val="'.intval($f['size']).'"/>'); |
||
296 | if (!empty($f['color'])) { |
||
297 | $file->write('<color rgb="'.strval($f['color']).'"/>'); |
||
298 | } |
||
299 | if (!empty($f['bold'])) { |
||
300 | $file->write('<b val="true"/>'); |
||
301 | } |
||
302 | if (!empty($f['italic'])) { |
||
303 | $file->write('<i val="true"/>'); |
||
304 | } |
||
305 | if (!empty($f['underline'])) { |
||
306 | $file->write('<u val="single"/>'); |
||
307 | } |
||
308 | if (!empty($f['strike'])) { |
||
309 | $file->write('<strike val="true"/>'); |
||
310 | } |
||
311 | $file->write('</font>'); |
||
312 | } |
||
313 | } |
||
314 | $file->write('</fonts>'); |
||
315 | |||
316 | $file->write('<fills count="'.(count($fills)).'">'); |
||
317 | $file->write('<fill><patternFill patternType="none"/></fill>'); |
||
318 | $file->write('<fill><patternFill patternType="gray125"/></fill>'); |
||
319 | foreach ($fills as $fill) { |
||
320 | if (!empty($fill)) { //fills have 2 empty placeholders in array to offset the 2 static xml entries above |
||
321 | $file->write('<fill><patternFill patternType="solid"><fgColor rgb="'.strval($fill).'"/><bgColor indexed="64"/></patternFill></fill>'); |
||
322 | } |
||
323 | } |
||
324 | $file->write('</fills>'); |
||
325 | |||
326 | $file->write('<borders count="'.(count($borders)).'">'); |
||
327 | $file->write('<border diagonalDown="false" diagonalUp="false"><left/><right/><top/><bottom/><diagonal/></border>'); |
||
328 | foreach ($borders as $border) { |
||
329 | if (!empty($border)) { //fonts have an empty placeholder in the array to offset the static xml entry above |
||
330 | $pieces = json_decode($border, true); |
||
331 | $border_style = !empty($pieces['style']) ? $pieces['style'] : 'hair'; |
||
332 | $border_color = !empty($pieces['color']) ? '<color rgb="'.strval($pieces['color']).'"/>' : ''; |
||
333 | $file->write('<border diagonalDown="false" diagonalUp="false">'); |
||
334 | foreach (['left', 'right', 'top', 'bottom'] as $side) { |
||
335 | $show_side = in_array($side, $pieces['side']) ? true : false; |
||
336 | $file->write($show_side ? "<$side style=\"$border_style\">$border_color</$side>" : "<$side/>"); |
||
337 | } |
||
338 | $file->write('<diagonal/>'); |
||
339 | $file->write('</border>'); |
||
340 | } |
||
341 | } |
||
342 | $file->write('</borders>'); |
||
343 | |||
344 | $file->write('<cellStyleXfs count="20">'); |
||
345 | $file->write('<xf applyAlignment="true" applyBorder="true" applyFont="true" applyProtection="true" borderId="0" fillId="0" fontId="0" numFmtId="164">'); |
||
346 | $file->write('<alignment horizontal="general" indent="0" shrinkToFit="false" textRotation="0" vertical="bottom" wrapText="false"/>'); |
||
347 | $file->write('<protection hidden="false" locked="true"/>'); |
||
348 | $file->write('</xf>'); |
||
349 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>'); |
||
350 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="0"/>'); |
||
351 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>'); |
||
352 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="2" numFmtId="0"/>'); |
||
353 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
354 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
355 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
356 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
357 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
358 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
359 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
360 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
361 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
362 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="0" numFmtId="0"/>'); |
||
363 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="43"/>'); |
||
364 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="41"/>'); |
||
365 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="44"/>'); |
||
366 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="42"/>'); |
||
367 | $file->write('<xf applyAlignment="false" applyBorder="false" applyFont="true" applyProtection="false" borderId="0" fillId="0" fontId="1" numFmtId="9"/>'); |
||
368 | $file->write('</cellStyleXfs>'); |
||
369 | |||
370 | $file->write('<cellXfs count="'.(count($styleIndexes)).'">'); |
||
371 | foreach ($styleIndexes as $v) { |
||
372 | $applyAlignment = isset($v['alignment']) ? 'true' : 'false'; |
||
373 | $wrapText = !empty($v['wrap_text']) ? 'true' : 'false'; |
||
374 | $horizAlignment = isset($v['halign']) ? $v['halign'] : 'general'; |
||
375 | $vertAlignment = isset($v['valign']) ? $v['valign'] : 'bottom'; |
||
376 | $applyBorder = isset($v['border_idx']) ? 'true' : 'false'; |
||
377 | $applyFont = 'true'; |
||
378 | $borderIdx = isset($v['border_idx']) ? intval($v['border_idx']) : 0; |
||
379 | $fillIdx = isset($v['fill_idx']) ? intval($v['fill_idx']) : 0; |
||
380 | $fontIdx = isset($v['font_idx']) ? intval($v['font_idx']) : 0; |
||
381 | $file->write('<xf applyAlignment="'.$applyAlignment.'" applyBorder="'.$applyBorder.'" applyFont="'.$applyFont.'" applyProtection="false" borderId="'.($borderIdx).'" fillId="'.($fillIdx).'" fontId="'.($fontIdx).'" numFmtId="'.(164 + $v['num_fmt_idx']).'" xfId="0">'); |
||
382 | $file->write(' <alignment horizontal="'.$horizAlignment.'" vertical="'.$vertAlignment.'" textRotation="0" wrapText="'.$wrapText.'" indent="0" shrinkToFit="false"/>'); |
||
383 | $file->write(' <protection locked="true" hidden="false"/>'); |
||
384 | $file->write('</xf>'); |
||
385 | } |
||
386 | $file->write('</cellXfs>'); |
||
387 | $file->write('<cellStyles count="6">'); |
||
388 | $file->write('<cellStyle builtinId="0" customBuiltin="false" name="Normal" xfId="0"/>'); |
||
389 | $file->write('<cellStyle builtinId="3" customBuiltin="false" name="Comma" xfId="15"/>'); |
||
390 | $file->write('<cellStyle builtinId="6" customBuiltin="false" name="Comma [0]" xfId="16"/>'); |
||
391 | $file->write('<cellStyle builtinId="4" customBuiltin="false" name="Currency" xfId="17"/>'); |
||
392 | $file->write('<cellStyle builtinId="7" customBuiltin="false" name="Currency [0]" xfId="18"/>'); |
||
393 | $file->write('<cellStyle builtinId="5" customBuiltin="false" name="Percent" xfId="19"/>'); |
||
394 | $file->write('</cellStyles>'); |
||
395 | $file->write('</styleSheet>'); |
||
396 | $file->close(); |
||
397 | |||
398 | return $temporaryFilename; |
||
399 | } |
||
400 | |||
401 | /** |
||
402 | * @param string $sheetName |
||
403 | * @param array $headerTypes |
||
404 | * @param array $colOptions |
||
405 | */ |
||
406 | public function buildHeader(string $sheetName, array $headerTypes, $colOptions = []) |
||
407 | { |
||
408 | if (empty($sheetName) || empty($headerTypes) || $this->hasSheet($sheetName)) { |
||
409 | return; |
||
410 | } |
||
411 | $this->initSheet($sheetName, $colOptions, $headerTypes); |
||
412 | $this->sheetName = $sheetName; |
||
413 | } |
||
414 | |||
415 | /** |
||
416 | * @param string $sheetName |
||
417 | * @param array $headerTypes |
||
418 | * @param array $colOptions |
||
419 | */ |
||
420 | protected function initSheet(string $sheetName, array $colOptions = [], array $headerTypes = []) |
||
421 | { |
||
422 | if ($this->sheetName === $sheetName || $this->hasSheet($sheetName)) { |
||
423 | return; |
||
424 | } |
||
425 | $style = $colOptions; |
||
426 | $colWidths = isset($colOptions['widths']) ? (array) $colOptions['widths'] : []; |
||
427 | $this->createSheet($sheetName, $colOptions); |
||
428 | $sheet = $this->getSheet($sheetName); |
||
429 | $sheet->initContent($colWidths, $this->isTabSelected()); |
||
430 | if (!empty($headerTypes)) { |
||
431 | $sheet->columns = $this->initColumnsTypes($headerTypes); |
||
432 | $headerRow = array_keys($headerTypes); |
||
433 | $writer = $sheet->getFileWriter(); |
||
434 | $writer->write( |
||
435 | '<row collapsed="false" customFormat="false" customHeight="false" hidden="false" ht="12.1" outlineLevel="0" r="'. 1 .'">' |
||
436 | ); |
||
437 | foreach ($headerRow as $c => $v) { |
||
438 | $cellStyleIdx = empty($style) ? |
||
439 | $sheet->columns[$c]['default_cell_style'] : |
||
440 | $this->addCellStyle('GENERAL', json_encode(isset($style[0]) ? $style[$c] : $style)); |
||
441 | $sheet->writeCell(0, $c, $v, 'n_string', $cellStyleIdx); |
||
442 | } |
||
443 | $writer->write('</row>'); |
||
444 | ++$sheet->rowCount; |
||
445 | } |
||
446 | |||
447 | $this->sheetName = $sheetName; |
||
448 | } |
||
449 | |||
450 | protected function initColumnsTypes($headerTypes) |
||
451 | { |
||
452 | foreach ($headerTypes as $v) { |
||
453 | $numberFormat = Support::numberFormatStandardized($v); |
||
454 | $cellStyleIdx = $this->addCellStyle($numberFormat, $styleString = null); |
||
455 | $columns[] = [ |
||
456 | 'number_format' => $numberFormat, //contains excel format like 'YYYY-MM-DD HH:MM:SS' |
||
457 | 'number_format_type' => Support::determineNumberFormatType($numberFormat), //contains friendly format like 'datetime' |
||
458 | 'default_cell_style' => $cellStyleIdx, |
||
459 | ]; |
||
460 | } |
||
461 | |||
462 | return $columns ?? []; |
||
463 | } |
||
464 | |||
465 | /** |
||
466 | * 是否第一个sheet. |
||
467 | * |
||
468 | * @return bool |
||
469 | */ |
||
470 | public function isTabSelected() |
||
471 | { |
||
472 | return 1 === count($this->sheets); |
||
473 | } |
||
474 | |||
475 | /** |
||
476 | * @param string|null $sheetName |
||
477 | * |
||
478 | * @return Sheet |
||
479 | */ |
||
480 | public function getSheet(string $sheetName = null): Sheet |
||
481 | { |
||
482 | if ($sheetName) { |
||
483 | $this->sheet = $this->sheets[$sheetName]; |
||
484 | } |
||
485 | |||
486 | return $this->sheet; |
||
487 | } |
||
488 | |||
489 | /** |
||
490 | * @param Sheet $sheet |
||
491 | */ |
||
492 | public function setSheet(Sheet $sheet) |
||
493 | { |
||
494 | $this->sheet = $sheet; |
||
495 | } |
||
496 | |||
497 | /** |
||
498 | * @param string $string |
||
499 | */ |
||
500 | protected function sheetWriter(string $string) |
||
501 | { |
||
502 | $this->sheet->fileWriter->write($string); |
||
503 | } |
||
504 | |||
505 | /** |
||
506 | * @param string $sheetName |
||
507 | * @param array $colOptions |
||
508 | */ |
||
509 | protected function createSheet(string $sheetName, array $colOptions = []) |
||
510 | { |
||
511 | $sheetFilename = $this->tempFilename(); |
||
512 | $sheetXmlName = 'sheet'.(count($this->sheets) + 1).'.xml'; |
||
513 | $autoFilter = isset($colOptions['auto_filter']) ? intval($colOptions['auto_filter']) : false; |
||
514 | $freezeRows = isset($colOptions['freeze_rows']) ? intval($colOptions['freeze_rows']) : false; |
||
515 | $freezeColumns = isset($colOptions['freeze_columns']) ? intval($colOptions['freeze_columns']) : false; |
||
516 | |||
517 | $this->sheets[$sheetName] = new Sheet( |
||
518 | [ |
||
519 | 'filename' => $sheetFilename, |
||
520 | 'sheetname' => $sheetName, |
||
521 | 'xmlname' => $sheetXmlName, |
||
522 | 'row_count' => 0, |
||
523 | 'columns' => [], |
||
524 | 'merge_cells' => [], |
||
525 | 'max_cell_tag_start' => 0, |
||
526 | 'max_cell_tag_end' => 0, |
||
527 | 'auto_filter' => $autoFilter, |
||
528 | 'freeze_rows' => $freezeRows, |
||
529 | 'freeze_columns' => $freezeColumns, |
||
530 | 'finalized' => false, |
||
531 | ], |
||
532 | $this->sheetWriter |
||
533 | ); |
||
534 | |||
535 | $this->sheet = $this->sheets[$sheetName]; |
||
536 | } |
||
537 | |||
538 | /** |
||
539 | * @param string $sheetName |
||
540 | * |
||
541 | * @return bool |
||
542 | */ |
||
543 | protected function hasSheet(string $sheetName) |
||
544 | { |
||
545 | if (isset($this->sheets[$sheetName])) { |
||
546 | $this->setSheet($this->sheets[$sheetName]); |
||
547 | |||
548 | return true; |
||
549 | } |
||
550 | |||
551 | return false; |
||
552 | } |
||
553 | |||
554 | /** |
||
555 | * @return Sheet[] |
||
556 | */ |
||
557 | public function getSheets() |
||
558 | { |
||
559 | return $this->sheets; |
||
560 | } |
||
561 | |||
562 | /** |
||
563 | * @param Sheet[] $sheets |
||
564 | */ |
||
565 | public function setSheets($sheets) |
||
566 | { |
||
567 | $this->sheets = $sheets; |
||
568 | } |
||
569 | |||
570 | /** |
||
571 | * @return bool|string |
||
572 | */ |
||
573 | protected function tempFilename() |
||
574 | { |
||
575 | $tempDir = !empty($this->tempDir) ? $this->tempDir : sys_get_temp_dir(); |
||
576 | $filename = tempnam($tempDir, 'xlsx_writer_'); |
||
577 | $this->tempFiles[] = $filename; |
||
578 | |||
579 | return $filename; |
||
580 | } |
||
581 | |||
582 | /** |
||
583 | * @param string $title |
||
584 | * |
||
585 | * @return $this |
||
586 | */ |
||
587 | public function setTitle($title = '') |
||
588 | { |
||
589 | $this->title = $title; |
||
590 | |||
591 | return $this; |
||
592 | } |
||
593 | |||
594 | /** |
||
595 | * @param string $subject |
||
596 | * |
||
597 | * @return $this |
||
598 | */ |
||
599 | public function setSubject($subject = '') |
||
600 | { |
||
601 | $this->subject = $subject; |
||
602 | |||
603 | return $this; |
||
604 | } |
||
605 | |||
606 | /** |
||
607 | * @param string $author |
||
608 | * |
||
609 | * @return $this |
||
610 | */ |
||
611 | public function setAuthor($author = '') |
||
612 | { |
||
613 | $this->author = $author; |
||
614 | |||
615 | return $this; |
||
616 | } |
||
617 | |||
618 | /** |
||
619 | * @param string $company |
||
620 | * |
||
621 | * @return $this |
||
622 | */ |
||
623 | public function setCompany($company = '') |
||
624 | { |
||
625 | $this->company = $company; |
||
626 | |||
627 | return $this; |
||
628 | } |
||
629 | |||
630 | /** |
||
631 | * @param array $keywords |
||
632 | * |
||
633 | * @return $this |
||
634 | */ |
||
635 | public function setKeywords(array $keywords = []) |
||
636 | { |
||
637 | $this->keywords = $keywords; |
||
638 | |||
639 | return $this; |
||
640 | } |
||
641 | |||
642 | /** |
||
643 | * @param string $description |
||
644 | * |
||
645 | * @return $this |
||
646 | */ |
||
647 | public function setDescription($description = '') |
||
648 | { |
||
649 | $this->description = $description; |
||
650 | |||
651 | return $this; |
||
652 | } |
||
653 | |||
654 | public function __destruct() |
||
655 | { |
||
656 | if (!empty($this->tempFiles)) { |
||
657 | foreach ($this->tempFiles as $tempFile) { |
||
658 | /* @scrutinizer ignore-unhandled */ |
||
659 | @unlink($tempFile); |
||
660 | } |
||
661 | } |
||
662 | } |
||
663 | } |
||
664 |
If you suppress an error, we recommend checking for the error condition explicitly: