diff --git a/README.md b/README.md
index c2d10bf..0c151bf 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# SimpleXLSXGen class 0.9.21 (Official)
+# SimpleXLSXGen class 0.9.22 (Official)
[](https://www.patreon.com/shuchkin) [](https://github.com/shuchkin/simplexlsxgen/blob/master/license.md) [](https://github.com/shuchkin/simplexlsxgen/stargazers) [](https://github.com/shuchkin/simplexlsxgen/network) [](https://github.com/shuchkin/simplexlsxgen/issues)
Export data to Excel XLSX file. PHP XLSX generator. No external tools and libraries.
@@ -16,12 +16,9 @@ $books = [
[908606664, 'Slinky Malinki', 'Lynley Dodd', 'Mallinson Rendel', 'NZ']
];
$xlsx = SimpleXLSXGen::fromArray( $books );
-$xlsx->saveAs('books.xlsx');
+$xlsx->saveAs('books.xlsx'); // or downloadAs('books.xlsx')
```
![XLSX screenshot](books.png)
-```
-// SimpleXLSXGen::download() or SimpleXSLSXGen::downloadAs('table.xlsx');
-```
## Installation
The recommended way to install this library is [through Composer](https://getcomposer.org).
@@ -49,7 +46,19 @@ $data = [
SimpleXLSXGen::fromArray( $data )->saveAs('datatypes.xlsx');
```
![XLSX screenshot](datatypes.png)
-
+### Fluid examples
+```php
+SimpleXLSXGen::fromArray( $books )->downloadAs('table.xlsx'); // output to browser for download
+SimpleXLSXGen::fromArray( $books )->addSheet( $books2 )->download(); // multiple sheets
+(new SimpleXLSXGen)->addSheet( $books, 'Modern style')->save();
+```
+### Old school, multiple sheets
+```php
+$xlsx = new SimpleXLSXGen();
+$xlsx->addSheet( $books, 'Catalog 2021' );
+$xlsx->addSheet( $books2, 'Stephen King catalog');
+$xlsx->downloadAs('books_2021.xlsx');
+```
### Debug
```php
ini_set('error_reporting', E_ALL );
@@ -63,6 +72,7 @@ SimpleXLSXGen::fromArray( $data )->saveAs('debug.xlsx');
## History
+v0.9.22 (2020-11-04) Added multiple sheets support, thx [Savino59](https://github.com/Savino59), class ready for extend now
v0.9.21 (2020-10-17) Updated images
v0.9.20 (2020-10-04) Disable type detection if string started with chr(0)
v0.9.19 (2020-08-23) Numbers like SKU right aligned now
diff --git a/src/SimpleXLSXGen.php b/src/SimpleXLSXGen.php
index de5c316..44806c8 100644
--- a/src/SimpleXLSXGen.php
+++ b/src/SimpleXLSXGen.php
@@ -8,16 +8,15 @@
class SimpleXLSXGen {
public $curSheet;
- private $sheets;
- private $template;
- private $SI, $SI_KEYS;
+ protected $sheets;
+ protected $template;
+ protected $SI, $SI_KEYS;
public function __construct() {
- $this->curSheet = 0;
- $this->sheets[0]['rows'] = [];
- $this->sheets[0]['Name'] = 'Sheet1';
- $this->SI = []; // sharedStrings index & keys
- $this->SI_KEYS = [];
+ $this->curSheet = -1;
+ $this->sheets = [ ['name' => 'Sheet1', 'rows' => [] ] ];
+ $this->SI = []; // sharedStrings index
+ $this->SI_KEYS = []; // & keys
$this->template = [
'[Content_Types].xml' => '
@@ -83,35 +82,22 @@ class SimpleXLSXGen {
// 0100
1200
// Простой шаблонБудем делать генератор
}
-
- public function setSheet($idx, $name = null) {
- if (!isset($this->sheets[$idx])) {
- $this->sheets[$idx] = [];
- $this->sheets[$idx]['rows'] = [];
- }
- $this->curSheet = $idx;
- if ($name)
- $this->sheets[$idx]['Name'] = $name;
- else {
- if (!isset($this->sheets[$idx]['Name'])) {
- $this->sheets[$idx]['Name'] = 'Sheet'.$idx;
- }
- }
- return $this->sheets[$idx]['Name'];
+ public static function fromArray( array $rows, $sheetName = null ) {
+ $xlsx = new static();
+ return $xlsx->addSheet( $rows, $sheetName );
}
- public static function fromArray( array $rows ) {
- $xlsx = new self();
- $xlsx->setRows( $rows );
- return $xlsx;
- }
+ public function addSheet( array $rows, $name = null ) {
+ $this->curSheet++;
+
+ $this->sheets[$this->curSheet] = ['name' => $name ?: 'Sheet'.($this->curSheet+1)];
- public function setRows( $rows ) {
if ( is_array( $rows ) && isset( $rows[0] ) && is_array($rows[0]) ) {
- $this->sheets[$this->curSheet]['rows']=$rows;
+ $this->sheets[$this->curSheet]['rows'] = $rows;
} else {
- $this->sheets[$this->curSheet]['rows']= [];
+ $this->sheets[$this->curSheet]['rows'] = [];
}
+ return $this;
}
public function __toString() {
@@ -120,7 +106,7 @@ class SimpleXLSXGen {
return '';
}
- if ( ! $this->_generate( $fh ) ) {
+ if ( ! $this->_write( $fh ) ) {
fclose( $fh );
return '';
}
@@ -135,7 +121,7 @@ class SimpleXLSXGen {
if (!$fh) {
return false;
}
- if ( !$this->_generate($fh) ) {
+ if ( !$this->_write($fh) ) {
fclose($fh);
return false;
}
@@ -154,7 +140,7 @@ class SimpleXLSXGen {
return false;
}
- if ( !$this->_generate( $fh )) {
+ if ( !$this->_write( $fh )) {
fclose( $fh );
return false;
}
@@ -176,7 +162,96 @@ class SimpleXLSXGen {
return true;
}
- private function _saveIt($fh, &$cdrec, $cfilename, &$data) {
+ protected function _write( $fh ) {
+
+
+ $dirSignatureE= "\x50\x4b\x05\x06"; // end of central dir signature
+ $zipComments = 'Generated by '.__CLASS__.' PHP class, thanks sergey.shuchkin@gmail.com';
+
+ if (!$fh) {
+ return false;
+ }
+
+ $cdrec = ''; // central directory content
+ $entries= 0; // number of zipped files
+ $cnt_sheets = count( $this->sheets );
+
+ foreach ($this->template as $cfilename => $template ) {
+ if ( $cfilename === '[Content_Types].xml' ) {
+ $s = '';
+ for ( $i = 0; $i < $cnt_sheets; $i++) {
+ $s .= '';
+ }
+ $template = str_replace('{SHEETS}', $s, $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ }
+ elseif ( $cfilename === 'xl/_rels/workbook.xml.rels' ) {
+ $s = '';
+ for ( $i = 0; $i < $cnt_sheets; $i++) {
+ $s .= '\n";
+ }
+ $s .= '';
+ $template = str_replace('{SHEETS}', $s, $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ }
+ elseif ( $cfilename === 'xl/workbook.xml' ) {
+ $s = '';
+ foreach ( $this->sheets as $k => $v ) {
+ $s .= '';
+ }
+ $template = str_replace('{SHEETS}', $s, $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ }
+ elseif ( $cfilename === 'docProps/core.xml' ) {
+ $template = str_replace('{DATE}', gmdate('Y-m-d\TH:i:s\Z'), $template);
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ( $cfilename === 'xl/sharedStrings.xml' ) {
+ if (!count($this->SI)) {
+ $this->SI[] = 'No Data';
+ }
+ $si_cnt = count($this->SI);
+ $this->SI = ''.implode("\r\n", $this->SI).'';
+ $template = str_replace(['{CNT}', '{STRINGS}'], [ $si_cnt, $this->SI ], $template );
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ } elseif ( $cfilename === 'xl/worksheets/sheet1.xml' ) {
+ foreach ( $this->sheets as $k => $v ) {
+ $filename = 'xl/worksheets/sheet'.($k+1).'.xml';
+ $xml = $this->_sheetToXML($this->sheets[$k], $template);
+ $this->_writeEntry($fh, $cdrec, $filename, $xml );
+ $entries++;
+ }
+ $xml = null;
+ }
+ else {
+ $this->_writeEntry($fh, $cdrec, $cfilename, $template);
+ $entries++;
+ }
+ }
+ $before_cd = ftell($fh);
+ fwrite($fh, $cdrec);
+
+ // end of central dir
+ fwrite($fh, $dirSignatureE);
+ fwrite($fh, pack('v', 0)); // number of this disk
+ fwrite($fh, pack('v', 0)); // number of the disk with the start of the central directory
+ fwrite($fh, pack('v', $entries)); // total # of entries "on this disk"
+ fwrite($fh, pack('v', $entries)); // total # of entries overall
+ fwrite($fh, pack('V', mb_strlen($cdrec,'8bit'))); // size of central dir
+ fwrite($fh, pack('V', $before_cd)); // offset to start of central dir
+ fwrite($fh, pack('v', mb_strlen($zipComments,'8bit'))); // .zip file comment length
+ fwrite($fh, $zipComments);
+
+ return true;
+ }
+
+ protected function _writeEntry($fh, &$cdrec, $cfilename, $data) {
$zipSignature = "\x50\x4b\x03\x04"; // local file header signature
$dirSignature = "\x50\x4b\x01\x02"; // central dir header signature
@@ -215,7 +290,6 @@ class SimpleXLSXGen {
$e['offset'] = ftell($fh);
- /** @noinspection DisconnectedForeachInstructionInspection */
fwrite($fh, $zipSignature);
fwrite($fh, pack('s', $e['vneeded'])); // version_needed
fwrite($fh, pack('s', $e['bitflag'])); // general_bit_flag
@@ -226,7 +300,6 @@ class SimpleXLSXGen {
fwrite($fh, pack('I', $e['comsize'])); // compressed_size
fwrite($fh, pack('I', $e['uncsize'])); // uncompressed_size
fwrite($fh, pack('s', mb_strlen($cfilename, '8bit'))); // file_name_length
- /** @noinspection DisconnectedForeachInstructionInspection */
fwrite($fh, pack('s', 0)); // extra_field_length
fwrite($fh, $cfilename); // file_name
// ignoring extra_field
@@ -257,7 +330,7 @@ class SimpleXLSXGen {
$cdrec .= $e['comments'];
}
- private function worksheetEncode(&$sheet, &$data) {
+ protected function _sheetToXML(&$sheet, $template) {
$COLS = [];
$ROWS = [];
@@ -343,7 +416,7 @@ class SimpleXLSXGen {
}
$row .= ''
- .($ct === 'inlineStr' ? ''.$cv.'' : '' . $cv . '')."\r\n";
+ .($ct === 'inlineStr' ? ''.$cv.'' : '' . $cv . '')."\r\n";
}
$ROWS[] = $row . "\r\n";
}
@@ -357,96 +430,8 @@ class SimpleXLSXGen {
$REF = 'A1:A1';
}
return str_replace(['{REF}','{COLS}','{ROWS}'],
- [ $REF, implode("\r\n", $COLS), implode("\r\n",$ROWS) ],
- $data );
- }
-
- private function _generate( $fh ) {
-
-
- $dirSignatureE= "\x50\x4b\x05\x06"; // end of central dir signature
- $zipComments = 'Generated by '.__CLASS__.' PHP class, thanks sergey.shuchkin@gmail.com';
-
- if (!$fh) {
- return false;
- }
-
- $cdrec = ''; // central directory content
- $entries= 0; // number of zipped files
-
- foreach ($this->template as $cfilename => $data ) {
- if ( $cfilename === '[Content_Types].xml' ) {
- $s = '';
- for ($i=0; $i < count($this->sheets); $i++) {
- $s .= '';
- }
- $data = str_replace('{SHEETS}', $s, $data);
- $this->_saveIt($fh, $cdrec, $cfilename, $data);
- $entries++;
- }
- elseif ( $cfilename === 'xl/_rels/workbook.xml.rels' ) {
- $s = '';
- for ($i=0; $i < count($this->sheets); $i++) {
- $s .= '\n";
- }
- $s .= '';
- $data = str_replace('{SHEETS}', $s, $data);
- $this->_saveIt($fh, $cdrec, $cfilename, $data);
- $entries++;
- }
- elseif ( $cfilename === 'xl/workbook.xml' ) {
- $s = '';
- for ($i=0; $i < count($this->sheets); $i++) {
- $s .= '';
- }
- $data = str_replace('{SHEETS}', $s, $data);
- $this->_saveIt($fh, $cdrec, $cfilename, $data);
- $entries++;
- }
- elseif ( $cfilename === 'docProps/core.xml' ) {
- $data = str_replace('{DATE}', gmdate('Y-m-d\TH:i:s\Z'), $data);
- $this->_saveIt($fh, $cdrec, $cfilename, $data);
- $entries++;
- } elseif ( $cfilename === 'xl/sharedStrings.xml' ) {
- if (!count($this->SI)) {
- $this->SI[] = 'No Data';
- }
- $si_cnt = count($this->SI);
- $this->SI = ''.implode("\r\n", $this->SI).'';
- $data = str_replace(['{CNT}', '{STRINGS}'], [ $si_cnt, $this->SI ], $data );
- $this->_saveIt($fh, $cdrec, $cfilename, $data);
- $entries++;
- } elseif ( $cfilename === 'xl/worksheets/sheet1.xml' ) {
- for ($i=0; $i < count($this->sheets); $i++) {
- $filename = 'xl/worksheets/sheet'.($i+1).'.xml';
- $data1 = $this->worksheetEncode($this->sheets[$i], $data);
- $this->_saveIt($fh, $cdrec, $filename, $data1);
- $entries++;
- }
- $data1=null;
- }
- else {
- $this->_saveIt($fh, $cdrec, $cfilename, $data);
- $entries++;
- }
- }
- $before_cd = ftell($fh);
- fwrite($fh, $cdrec);
-
- // end of central dir
- fwrite($fh, $dirSignatureE);
- fwrite($fh, pack('v', 0)); // number of this disk
- fwrite($fh, pack('v', 0)); // number of the disk with the start of the central directory
- fwrite($fh, pack('v', $entries)); // total # of entries "on this disk"
- fwrite($fh, pack('v', $entries)); // total # of entries overall
- fwrite($fh, pack('V', mb_strlen($cdrec,'8bit'))); // size of central dir
- fwrite($fh, pack('V', $before_cd)); // offset to start of central dir
- fwrite($fh, pack('v', mb_strlen($zipComments,'8bit'))); // .zip file comment length
- fwrite($fh, $zipComments);
-
- return true;
+ [ $REF, implode("\r\n", $COLS), implode("\r\n",$ROWS) ],
+ $template );
}
public function num2name($num) {