├── LICENSE ├── README.md └── xlsxwriter.class.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Mark Jones 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PHP_XLSXWriter_plus 2 | ============== 3 | 4 | This library is designed to be lightweight, and have relatively low memory usage. This is the fork of https://github.com/mk-j/PHP_XLSXWriter 5 | 6 | It is designed to output an Excel spreadsheet in with (Office 2007+) xlsx format, with just basic features supported: 7 | * assumes input is valid UTF-8 8 | * multiple worksheets 9 | * supports cell formats: 10 | ``` 11 | // 1 0 12 | // 2 0.00 13 | // 3 #,##0 14 | // 4 #,##0.00 15 | // 5 $#,##0_);($#,##0) 16 | // 6 $#,##0_);[Red]($#,##0) 17 | // 7 $#,##0.00_);($#,##0.00) 18 | // 8 $#,##0.00_);[Red]($#,##0.00) 19 | // 9 0% 20 | // 10 0.00% 21 | // 11 0.00E+00 22 | // 12 # ?/? 23 | // 13 # ??/?? 24 | // 14 m/d/yyyy 25 | // 15 d-mmm-yy 26 | // 16 d-mmm 27 | // 17 mmm-yy 28 | // 18 h:mm AM/PM 29 | // 19 h:mm:ss AM/PM 30 | // 20 h:mm 31 | // 21 h:mm:ss 32 | // 22 m/d/yyyy h:mm 33 | // 37 #,##0_);(#,##0) 34 | // 38 #,##0_);[Red](#,##0) 35 | // 39 #,##0.00_);(#,##0.00) 36 | // 40 #,##0.00_);[Red](#,##0.00) 37 | // 45 mm:ss 38 | // 46 [h]:mm:ss 39 | // 47 mm:ss.0 40 | // 48 ##0.0E+0 41 | // 49 @ 42 | ``` 43 | * supports styling cells 44 | 45 | 46 | Simple example: 47 | ```php 48 | $data = array( 49 | array('year','month','amount'), 50 | array('2003','1','220'), 51 | array('2003','2','153.5'), 52 | ); 53 | 54 | $writer = new XLSXWriter(); 55 | $writer->writeSheet($data); 56 | $writer->writeToFile('output.xlsx'); 57 | ``` 58 | 59 | Multiple Sheets: 60 | ```php 61 | $data1 = array( 62 | array('5','3'), 63 | array('1','6'), 64 | ); 65 | $data2 = array( 66 | array('2','7','9'), 67 | array('4','8','0'), 68 | ); 69 | 70 | $writer = new XLSXWriter(); 71 | $writer->setAuthor('Doc Author'); 72 | $writer->writeSheet($data1); 73 | $writer->writeSheet($data2); 74 | echo $writer->writeToString(); 75 | ``` 76 | 77 | Cell Formatting: 78 | ```php 79 | $header = array( 80 | 'create_date'=>'string', 81 | 'quantity'=>'string', 82 | 'product_id'=>'string', 83 | 'amount'=>'string', 84 | 'description'=>'string', 85 | ); 86 | $data = array( 87 | array('2013-01-01',1,27,'44.00','twig'), 88 | array('2013-01-05',1,'=C1','-44.00','refund'), 89 | ); 90 | 91 | $writer = new XLSXWriter(); 92 | $writer->writeSheet($data,'Sheet1', $header, $style); 93 | $writer->writeToFile('example.xlsx'); 94 | ``` 95 | 96 | Cell Styling 97 | ```php 98 | $writer->setFontName('MS Sans Serif'); //default document font name 99 | $writer->setFontSize(8); //default document font size 100 | $writer->setWrapText(true); //default document wrap cells 101 | $writer->setVerticalAlign('top'); //default document vertical align 102 | $writer->setHorizontalAlign('left'); //default document horizontal alilgn 103 | $writer->setStartRow(10); //set start data filling row 104 | $writer->setStartCol(0); //set start data filling column 105 | 106 | //set styles 107 | $writer->writeSheet($data, 'Sheet1', $header, 108 | array ( 109 | array( // in each style element you can use or 'cells', or 'rows' or 'columns'. 110 | 'font' => array( 111 | 'name' => 'Times New Roman', 112 | 'size' => '11', 113 | 'color' => '0000FF', 114 | 'bold' => true, 115 | 'italic' => true, 116 | 'underline' => true), 117 | 'border' => array( 118 | 'style' => 'thin', 119 | 'color' => 'A0A0A0'), 120 | 'fill' => array( 121 | 'color' => 'F0F0F0'), 122 | 'cells' => array( //for 1 cell - array is not nessesary, use - 'cells' => 'C3' 123 | 'E1', 124 | 'E2'), 125 | 'wrapText' => true, 126 | 'verticalAlign' => 'top', 127 | 'horizontalAlign' => 'left', 128 | 'format' => 5 129 | ), 130 | array( 131 | 'fill' => array( 132 | 'color' => 'F09900'), 133 | 'columns' => '2' //for only one and firs column dont use 'columns' => '0', use 'columns' => array('0') 134 | ), 135 | array( 136 | 'fill' => array( 137 | 'color' => 'F000F0'), 138 | 'rows' => array( 139 | '0','1') //for only one and firs row dont use 'rows' => '0', use 'rows' => array('0') 140 | ) 141 | array( 142 | 'fill' => array( 143 | 'color' => 'F000F0'), 144 | 'allfilleddata' => true //for all filled data cells - can be used without: columns, rows, cells! 145 | ) 146 | ) 147 | ); 148 | ``` -------------------------------------------------------------------------------- /xlsxwriter.class.php: -------------------------------------------------------------------------------- 1 | author=$author; } 48 | public function setFontName($defaultFontName) { $this->defaultFontName=$defaultFontName; } 49 | public function setFontSize($defaultFontSize) { $this->defaultFontSize=$defaultFontSize; } 50 | public function setWrapText($defaultWrapText) { $this->defaultWrapText=$defaultWrapText; } 51 | public function setVerticalAlign($defaultVerticalAlign) { $this->defaultVerticalAlign=$defaultVerticalAlign; } 52 | public function setHorizontalAlign($defaultHorizontalAlign) { $this->defaultHorizontalAlign=$defaultHorizontalAlign; } 53 | private function setStyle($defaultStyle) { $this->defaultStyle=$defaultStyle; } 54 | public function setStartRow($defaultStartRow) { $this->defaultStartRow=($defaultStartRow > 0) ? ((int)$defaultStartRow - 1) : 0; } 55 | public function setStartCol($defaultStartCol) { $this->defaultStartCol=($defaultStartCol > 0) ? ((int)$defaultStartCol - 1) : 0; } 56 | 57 | public function __destruct() 58 | { 59 | if (!empty($this->temp_files)) { 60 | foreach($this->temp_files as $temp_file) { 61 | @unlink($temp_file); 62 | } 63 | } 64 | } 65 | 66 | protected function tempFilename() 67 | { 68 | $filename = tempnam("/tmp", "xlsx_writer_"); 69 | $this->temp_files[] = $filename; 70 | return $filename; 71 | } 72 | 73 | public function writeToStdOut() 74 | { 75 | $temp_file = $this->tempFilename(); 76 | self::writeToFile($temp_file); 77 | readfile($temp_file); 78 | } 79 | 80 | public function writeToString() 81 | { 82 | $temp_file = $this->tempFilename(); 83 | self::writeToFile($temp_file); 84 | $string = file_get_contents($temp_file); 85 | return $string; 86 | } 87 | 88 | public function writeToFile($filename) 89 | { 90 | @unlink($filename);//if the zip already exists, overwrite it 91 | $zip = new ZipArchive(); 92 | if (empty($this->sheets_meta)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", no worksheets defined."); return; } 93 | if (!$zip->open($filename, ZipArchive::CREATE)) { self::log("Error in ".__CLASS__."::".__FUNCTION__.", unable to create zip."); return; } 94 | 95 | $zip->addEmptyDir("docProps/"); 96 | $zip->addFromString("docProps/app.xml" , self::buildAppXML() ); 97 | $zip->addFromString("docProps/core.xml", self::buildCoreXML()); 98 | 99 | $zip->addEmptyDir("_rels/"); 100 | $zip->addFromString("_rels/.rels", self::buildRelationshipsXML()); 101 | 102 | $zip->addEmptyDir("xl/worksheets/"); 103 | foreach($this->sheets_meta as $sheet_meta) { 104 | $zip->addFile($sheet_meta['filename'], "xl/worksheets/".$sheet_meta['xmlname'] ); 105 | } 106 | if (!empty($this->shared_strings)) { 107 | $zip->addFile($this->writeSharedStringsXML(), "xl/sharedStrings.xml" ); //$zip->addFromString("xl/sharedStrings.xml", self::buildSharedStringsXML() ); 108 | } 109 | $zip->addFromString("xl/workbook.xml" , self::buildWorkbookXML() ); 110 | $zip->addFile($this->writeStylesXML(), "xl/styles.xml" ); //$zip->addFromString("xl/styles.xml" , self::buildStylesXML() ); 111 | $zip->addFromString("[Content_Types].xml" , self::buildContentTypesXML() ); 112 | 113 | $zip->addEmptyDir("xl/_rels/"); 114 | $zip->addFromString("xl/_rels/workbook.xml.rels", self::buildWorkbookRelsXML() ); 115 | $zip->close(); 116 | } 117 | 118 | 119 | public function writeSheet(array $data, $sheet_name='', array $header_types=array(), array $styles ) 120 | { 121 | for ($i = 0; $i < count($styles); $i++) { 122 | $styles[$i] += array('sheet' => $sheet_name); 123 | } 124 | $this->setStyle(array_merge((array)$this->defaultStyle, (array)$styles)); 125 | 126 | $data = empty($data) ? array( array('') ) : $data; 127 | 128 | $sheet_filename = $this->tempFilename(); 129 | $sheet_default = 'Sheet'.(count($this->sheets_meta)+1); 130 | $sheet_name = !empty($sheet_name) ? $sheet_name : $sheet_default; 131 | $this->sheets_meta[] = array('filename'=>$sheet_filename, 'sheetname'=>$sheet_name ,'xmlname'=>strtolower($sheet_default).".xml" ); 132 | 133 | $header_offset = empty($header_types) ? 0 : $this->defaultStartRow + 1; 134 | $row_count = count($data) + $header_offset; 135 | $column_count = count($data[self::array_first_key($data)]); 136 | $max_cell = self::xlsCell( $row_count-1, $column_count-1 ); 137 | 138 | $tabselected = count($this->sheets_meta)==1 ? 'true' : 'false';//only first sheet is selected 139 | $cell_formats_arr = empty($header_types) ? array_fill(0, $column_count, 'string') : array_values($header_types); 140 | $header_row = empty($header_types) ? array() : array_keys($header_types); 141 | 142 | $fd = fopen($sheet_filename, "w+"); 143 | if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; } 144 | 145 | fwrite($fd,''."\n"); 146 | fwrite($fd,''); 147 | fwrite($fd, ''); 148 | fwrite($fd, ''); 149 | fwrite($fd, ''); 150 | fwrite($fd, ''); 151 | fwrite($fd, ''); 152 | fwrite($fd, ''); 153 | fwrite($fd, ''); 154 | fwrite($fd, ''); 155 | fwrite($fd, ''); 156 | fwrite($fd, ''); 157 | fwrite($fd, ''); 158 | fwrite($fd, ''); 159 | fwrite($fd, ''); 160 | if (!empty($header_row)) 161 | { 162 | fwrite($fd, ''); 168 | } 169 | foreach($data as $i=>$row) 170 | { 171 | fwrite($fd, ''); 177 | } 178 | fwrite($fd, ''); 179 | fwrite($fd, ''); 180 | fwrite($fd, ''); 181 | fwrite($fd, ''); 182 | fwrite($fd, ''); 183 | fwrite($fd, '&C&"Times New Roman,Regular"&12&A'); 184 | fwrite($fd, '&C&"Times New Roman,Regular"&12Page &P'); 185 | fwrite($fd, ''); 186 | fwrite($fd,''); 187 | fclose($fd); 188 | } 189 | 190 | protected function writeCell($fd, $row_number, $column_number, $value, $sheet_name) 191 | { 192 | $cell = self::xlsCell($row_number, $column_number); 193 | $s = '0'; 194 | if ($this->defaultStyle) { 195 | foreach ($this->defaultStyle as $key => $style) { 196 | if (isset($style['sheet'])) { 197 | if ($style['sheet'] == $sheet_name) { 198 | if (isset($style['allfilleddata'])) { 199 | $s = $key + 1; 200 | } else { 201 | if (isset($style['columns'])) { 202 | if (is_array($style['columns'])) { 203 | if (in_array($column_number, $style['columns'])) $s = $key + 1; 204 | } else { 205 | if ($column_number == $style['columns']) $s = $key + 1; 206 | } 207 | } elseif (isset($style['rows'])) { 208 | if (is_array($style['rows'])) { 209 | if (in_array($row_number, $style['rows'])) $s = $key + 1; 210 | } else { 211 | if ($row_number == $style['rows']) $s = $key + 1; 212 | } 213 | } elseif (isset($style['cells'])) { 214 | if (is_array($style['cells'])) { 215 | if (in_array($cell, $style['cells'])) $s = $key + 1; 216 | } else { 217 | if ($cell == $style['cells']) $s = $key + 1; 218 | } 219 | } 220 | } 221 | } 222 | } 223 | } 224 | } 225 | if (is_numeric($value)) { 226 | fwrite($fd,''.($value*1).'');//int,float, etc 227 | } else if ($cell_format=='date') { 228 | fwrite($fd,''.intval(self::convert_date_time($value)).''); 229 | } else if ($cell_format=='datetime') { 230 | fwrite($fd,''.self::convert_date_time($value).''); 231 | } else if ($value==''){ 232 | fwrite($fd,''); 233 | } else if ($value{0}=='='){ 234 | fwrite($fd,''.self::xmlspecialchars($value).''); 235 | } else if ($value!==''){ 236 | fwrite($fd,''.self::xmlspecialchars($this->setSharedString($value)).''); 237 | } 238 | } 239 | 240 | protected function writeStylesXML() 241 | { 242 | $tempfile = $this->tempFilename(); 243 | $fd = fopen($tempfile, "w+"); 244 | if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; } 245 | fwrite($fd, ''."\n"); 246 | fwrite($fd, ''); 247 | if ($this->defaultStyle) { 248 | foreach ($this->defaultStyle as $style) { 249 | if (isset($style['sheet'])) { 250 | if (isset($style['font'])) $this->fontsCount++; 251 | } 252 | } 253 | } 254 | fwrite($fd, ''); 255 | fwrite($fd, ' '); 256 | fwrite($fd, ' '); 257 | fwrite($fd, ' '); 258 | fwrite($fd, ' '); 259 | fwrite($fd, ' '); 260 | if ($this->defaultFontName == 'MS Sans Serif') { 261 | fwrite($fd, ' '); 262 | } else if ($this->defaultFontName == 'Calibri') { 263 | fwrite($fd, ' '); 264 | } else { 265 | fwrite($fd, ' '); 266 | } 267 | fwrite($fd, ' '); 268 | if ($this->defaultStyle) { 269 | foreach ($this->defaultStyle as $style) { 270 | if (isset($style['sheet'])) { 271 | if (isset($style['font'])) { 272 | if (isset($style['font']['name'])) $this->fontName = $style['font']['name']; 273 | if (isset($style['font']['size'])) $this->fontSize = $style['font']['size']; 274 | if (isset($style['font']['color'])) $this->fontColor = $style['font']['color']; 275 | if ($style['font']['bold']) $this->fontStyles .= ''; 276 | if ($style['font']['italic']) $this->fontStyles .= ''; 277 | if ($style['font']['underline']) $this->fontStyles .= ''; 278 | 279 | fwrite($fd, ' '); 280 | if ($this->fontStyles) fwrite($fd, ' '.$this->fontStyles); 281 | fwrite($fd, ' '); 282 | if ($this->fontColor) { 283 | fwrite($fd, ' '); 284 | } else { 285 | fwrite($fd, ' '); 286 | } 287 | fwrite($fd, ' '); 288 | fwrite($fd, ' '); 289 | if ($this->fontName == 'MS Sans Serif') { 290 | fwrite($fd, ' '); 291 | } else if ($this->fontName == 'Calibri') { 292 | fwrite($fd, ' '); 293 | } else { 294 | fwrite($fd, ' '); 295 | } 296 | fwrite($fd, ' '); 297 | } 298 | $this->fontStyles = ''; 299 | } 300 | } 301 | } 302 | fwrite($fd, ''); 303 | if ($this->defaultStyle) { 304 | foreach ($this->defaultStyle as $style) { 305 | if (isset($style['sheet'])) { 306 | if (isset($style['fill'])) $this->fillsCount++; 307 | } 308 | } 309 | } 310 | fwrite($fd, ''); 311 | fwrite($fd, ' '); 312 | fwrite($fd, ' '); 313 | if ($this->defaultStyle) { 314 | foreach ($this->defaultStyle as $style) { 315 | if (isset($style['sheet'])) { 316 | if (isset($style['fill'])) { 317 | if (isset($style['fill']['color'])) $this->fillColor = $style['fill']['color']; 318 | fwrite($fd, ' '); 319 | fwrite($fd, ' '); 320 | fwrite($fd, ' '); 321 | fwrite($fd, ' '); 322 | fwrite($fd, ' '); 323 | fwrite($fd, ' '); 324 | } 325 | } 326 | } 327 | } 328 | fwrite($fd, ''); 329 | if ($this->defaultStyle) { 330 | foreach ($this->defaultStyle as $style) { 331 | if (isset($style['sheet'])) { 332 | if (isset($style['border'])) $this->bordersCount++; 333 | } 334 | } 335 | } 336 | fwrite($fd, ''); 337 | fwrite($fd, ' '); 338 | fwrite($fd, ' '); 339 | fwrite($fd, ' '); 340 | if ($this->defaultStyle) { 341 | foreach ($this->defaultStyle as $style) { 342 | if (isset($style['sheet'])) { 343 | if (isset($style['border'])) { 344 | if (isset($style['border']['style'])) $this->bordersStyle = ' style="'.$style['border']['style'].'"'; 345 | if (isset($style['border']['color'])) $this->bordersColor = ''; 346 | fwrite($fd, ' '); 347 | fwrite($fd, ' bordersStyle.'>'.$this->bordersColor.''); 348 | fwrite($fd, ' bordersStyle.'>'.$this->bordersColor.''); 349 | fwrite($fd, ' bordersStyle.'>'.$this->bordersColor.''); 350 | fwrite($fd, ' bordersStyle.'>'.$this->bordersColor.''); 351 | fwrite($fd, ' '); 352 | fwrite($fd, ' '); 353 | } 354 | } 355 | } 356 | } 357 | fwrite($fd, ''); 358 | fwrite($fd, ''); 359 | fwrite($fd, ''); 360 | fwrite($fd, ''); 361 | $this->stylesCount += count($this->defaultStyle); 362 | fwrite($fd, ''); 363 | $this->defaultWrapText = ($this->defaultWrapText) ? '1' : '0'; 364 | fwrite($fd, ''); 365 | if ($this->defaultStyle) { 366 | foreach ($this->defaultStyle as $style) { 367 | if (isset($style['sheet'])) { 368 | if (isset($style['font'])) { 369 | $font_Id = $this->fontId += 1; 370 | } else { 371 | $font_Id = 0; 372 | } 373 | if (isset($style['fill'])) { 374 | $fill_Id = $this->fillId += 1; 375 | } else { 376 | $fill_Id = 0; 377 | } 378 | if (isset($style['border'])) { 379 | $border_Id = $this->borderId += 1; 380 | } else { 381 | $border_Id = 0; 382 | } 383 | if (isset($style['wrapText'])) { 384 | $wrapText = ($style['wrapText']) ? '1' : '0'; 385 | } else { 386 | $wrapText = $this->defaultWrapText; 387 | } 388 | 389 | $format_Id = (isset($style['format'])) ? $style['format'] : '0'; 390 | 391 | if (isset($style['verticalAlign'])) { 392 | $verticalAlign = $style['verticalAlign']; 393 | } else { 394 | $verticalAlign = $this->defaultVerticalAlign; 395 | } 396 | if (isset($style['horizontalAlign'])) { 397 | $horizontalAlign = $style['horizontalAlign']; 398 | } else { 399 | $horizontalAlign = $this->defaultHorizontalAlign; 400 | } 401 | fwrite($fd, ''); 402 | fwrite($fd, ''); 403 | fwrite($fd, ''); 404 | } 405 | } 406 | } 407 | fwrite($fd, ''); 408 | fwrite($fd, ''); 409 | fwrite($fd, ''); 410 | fwrite($fd, ''); 411 | fwrite($fd, ''); 412 | fwrite($fd, ''); 413 | fwrite($fd, ''); 414 | fwrite($fd, ''); 415 | fwrite($fd, ''); 416 | fwrite($fd, ''); 417 | fwrite($fd, ''); 418 | fwrite($fd, ''); 419 | fclose($fd); 420 | return $tempfile; 421 | } 422 | 423 | protected function setSharedString($v) 424 | { 425 | if (isset($this->shared_strings[$v])) 426 | { 427 | $string_value = $this->shared_strings[$v]; 428 | } 429 | else 430 | { 431 | $string_value = count($this->shared_strings); 432 | $this->shared_strings[$v] = $string_value; 433 | } 434 | $this->shared_string_count++;//non-unique count 435 | return $string_value; 436 | } 437 | 438 | protected function writeSharedStringsXML() 439 | { 440 | $tempfile = $this->tempFilename(); 441 | $fd = fopen($tempfile, "w+"); 442 | if ($fd===false) { self::log("write failed in ".__CLASS__."::".__FUNCTION__."."); return; } 443 | 444 | fwrite($fd,''."\n"); 445 | fwrite($fd,''); 446 | foreach($this->shared_strings as $s=>$c) 447 | { 448 | fwrite($fd,''.self::xmlspecialchars($s).''); 449 | } 450 | fwrite($fd, ''); 451 | fclose($fd); 452 | return $tempfile; 453 | } 454 | 455 | protected function buildAppXML() 456 | { 457 | $app_xml=""; 458 | $app_xml.=''."\n"; 459 | $app_xml.='0'; 460 | return $app_xml; 461 | } 462 | 463 | protected function buildCoreXML() 464 | { 465 | $core_xml=""; 466 | $core_xml.=''."\n"; 467 | $core_xml.=''; 468 | $core_xml.=''.date("Y-m-d\TH:i:s.00\Z").'';//$date_time = '2013-07-25T15:54:37.00Z'; 469 | $core_xml.=''.self::xmlspecialchars($this->author).''; 470 | $core_xml.='0'; 471 | $core_xml.=''; 472 | return $core_xml; 473 | } 474 | 475 | protected function buildRelationshipsXML() 476 | { 477 | $rels_xml=""; 478 | $rels_xml.=''."\n"; 479 | $rels_xml.=''; 480 | $rels_xml.=''; 481 | $rels_xml.=''; 482 | $rels_xml.=''; 483 | $rels_xml.="\n"; 484 | $rels_xml.=''; 485 | return $rels_xml; 486 | } 487 | 488 | protected function buildWorkbookXML() 489 | { 490 | $workbook_xml=""; 491 | $workbook_xml.=''."\n"; 492 | $workbook_xml.=''; 493 | $workbook_xml.=''; 494 | $workbook_xml.=''; 495 | $workbook_xml.=''; 496 | foreach($this->sheets_meta as $i=>$sheet_meta) { 497 | $workbook_xml.=''; 498 | } 499 | $workbook_xml.=''; 500 | $workbook_xml.=''; 501 | return $workbook_xml; 502 | } 503 | 504 | protected function buildWorkbookRelsXML() 505 | { 506 | $wkbkrels_xml=""; 507 | $wkbkrels_xml.=''."\n"; 508 | $wkbkrels_xml.=''; 509 | $wkbkrels_xml.=''; 510 | foreach($this->sheets_meta as $i=>$sheet_meta) { 511 | $wkbkrels_xml.=''; 512 | } 513 | if (!empty($this->shared_strings)) { 514 | $wkbkrels_xml.=''; 515 | } 516 | $wkbkrels_xml.="\n"; 517 | $wkbkrels_xml.=''; 518 | return $wkbkrels_xml; 519 | } 520 | 521 | protected function buildContentTypesXML() 522 | { 523 | $content_types_xml=""; 524 | $content_types_xml.=''."\n"; 525 | $content_types_xml.=''; 526 | $content_types_xml.=''; 527 | $content_types_xml.=''; 528 | foreach($this->sheets_meta as $i=>$sheet_meta) { 529 | $content_types_xml.=''; 530 | } 531 | if (!empty($this->shared_strings)) { 532 | $content_types_xml.=''; 533 | } 534 | $content_types_xml.=''; 535 | $content_types_xml.=''; 536 | $content_types_xml.=''; 537 | $content_types_xml.=''; 538 | $content_types_xml.="\n"; 539 | $content_types_xml.=''; 540 | return $content_types_xml; 541 | } 542 | 543 | //------------------------------------------------------------------ 544 | /* 545 | * @param $row_number int, zero based 546 | * @param $column_number int, zero based 547 | * @return Cell label/coordinates, ex: A1, C3, AA42 548 | * */ 549 | public static function xlsCell($row_number, $column_number) 550 | { 551 | $n = $column_number; 552 | for($r = ""; $n >= 0; $n = intval($n / 26) - 1) { 553 | $r = chr($n%26 + 0x41) . $r; 554 | } 555 | return $r . ($row_number+1); 556 | } 557 | //------------------------------------------------------------------ 558 | public static function log($string) 559 | { 560 | file_put_contents("php://stderr", date("Y-m-d H:i:s:").rtrim(is_array($string) ? json_encode($string) : $string)."\n"); 561 | } 562 | //------------------------------------------------------------------ 563 | public static function xmlspecialchars($val) 564 | { 565 | return str_replace("'", "'", htmlspecialchars($val)); 566 | } 567 | //------------------------------------------------------------------ 568 | public static function array_first_key(array $arr) 569 | { 570 | reset($arr); 571 | $first_key = key($arr); 572 | return $first_key; 573 | } 574 | //------------------------------------------------------------------ 575 | public static function convert_date_time($date_input) //thanks to Excel::Writer::XLSX::Worksheet.pm (perl) 576 | { 577 | $days = 0; # Number of days since epoch 578 | $seconds = 0; # Time expressed as fraction of 24h hours in seconds 579 | $year=$month=$day=0; 580 | $hour=$min =$sec=0; 581 | 582 | $date_time = $date_input; 583 | if (preg_match("/(\d{4})\-(\d{2})\-(\d{2})/", $date_time, $matches)) 584 | { 585 | list($junk,$year,$month,$day) = $matches; 586 | } 587 | if (preg_match("/(\d{2}):(\d{2}):(\d{2})/", $date_time, $matches)) 588 | { 589 | list($junk,$hour,$min,$sec) = $matches; 590 | $seconds = ( $hour * 60 * 60 + $min * 60 + $sec ) / ( 24 * 60 * 60 ); 591 | } 592 | 593 | //using 1900 as epoch, not 1904, ignoring 1904 special case 594 | 595 | # Special cases for Excel. 596 | if ("$year-$month-$day"=='1899-12-31') return $seconds ; # Excel 1900 epoch 597 | if ("$year-$month-$day"=='1900-01-00') return $seconds ; # Excel 1900 epoch 598 | if ("$year-$month-$day"=='1900-02-29') return 60 + $seconds ; # Excel false leapday 599 | 600 | # We calculate the date by calculating the number of days since the epoch 601 | # and adjust for the number of leap days. We calculate the number of leap 602 | # days by normalising the year in relation to the epoch. Thus the year 2000 603 | # becomes 100 for 4 and 100 year leapdays and 400 for 400 year leapdays. 604 | $epoch = 1900; 605 | $offset = 0; 606 | $norm = 300; 607 | $range = $year - $epoch; 608 | 609 | # Set month days and check for leap year. 610 | $leap = (($year % 400 == 0) || (($year % 4 == 0) && ($year % 100)) ) ? 1 : 0; 611 | $mdays = array( 31, ($leap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ); 612 | 613 | # Some boundary checks 614 | if($year < $epoch || $year > 9999) return 0; 615 | if($month < 1 || $month > 12) return 0; 616 | if($day < 1 || $day > $mdays[ $month - 1 ]) return 0; 617 | 618 | # Accumulate the number of days since the epoch. 619 | $days = $day; # Add days for current month 620 | $days += array_sum( array_slice($mdays, 0, $month-1 ) ); # Add days for past months 621 | $days += $range * 365; # Add days for past years 622 | $days += intval( ( $range ) / 4 ); # Add leapdays 623 | $days -= intval( ( $range + $offset ) / 100 ); # Subtract 100 year leapdays 624 | $days += intval( ( $range + $offset + $norm ) / 400 ); # Add 400 year leapdays 625 | $days -= $leap; # Already counted above 626 | 627 | # Adjust for Excel erroneously treating 1900 as a leap year. 628 | if ($days > 59) { $days++;} 629 | 630 | return $days + $seconds; 631 | } 632 | //------------------------------------------------------------------ 633 | } --------------------------------------------------------------------------------