mirror of
https://github.com/shuchkin/simplexlsxgen.git
synced 2023-08-10 21:12:59 +03:00
Multiple sheets support and class ready for extends now
This commit is contained in:
parent
07d3e91c28
commit
42558401ef
22
README.md
22
README.md
@ -1,4 +1,4 @@
|
||||
# SimpleXLSXGen class 0.9.21 (Official)
|
||||
# SimpleXLSXGen class 0.9.22 (Official)
|
||||
[<img src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fshieldsio-patreon.herokuapp.com%2Fshuchkin" />](https://www.patreon.com/shuchkin) [<img src="https://img.shields.io/github/license/shuchkin/simplexlsxgen" />](https://github.com/shuchkin/simplexlsxgen/blob/master/license.md) [<img src="https://img.shields.io/github/stars/shuchkin/simplexlsxgen" />](https://github.com/shuchkin/simplexlsxgen/stargazers) [<img src="https://img.shields.io/github/forks/shuchkin/simplexlsxgen" />](https://github.com/shuchkin/simplexlsxgen/network) [<img src="https://img.shields.io/github/issues/shuchkin/simplexlsxgen" />](https://github.com/shuchkin/simplexlsxgen/issues)
|
||||
|
||||
Export data to Excel XLSX file. PHP XLSX generator. No external tools and libraries.<br/>
|
||||
@ -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<br/>
|
||||
v0.9.21 (2020-10-17) Updated images<br/>
|
||||
v0.9.20 (2020-10-04) Disable type detection if string started with chr(0)<br/>
|
||||
v0.9.19 (2020-08-23) Numbers like SKU right aligned now<br/>
|
||||
|
@ -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' => '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
||||
@ -83,35 +82,22 @@ class SimpleXLSXGen {
|
||||
// <row r="1" spans="1:2" x14ac:dyDescent="0.35"><c r="A1" t="s"><v>0</v></c><c r="B1"><v>100</v></c></row><row r="2" spans="1:2" x14ac:dyDescent="0.35"><c r="A2" t="s"><v>1</v></c><c r="B2"><v>200</v></c></row>
|
||||
// <si><t>Простой шаблон</t></si><si><t>Будем делать генератор</t></si>
|
||||
}
|
||||
|
||||
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 .= '<Override PartName="/xl/worksheets/sheet'.($i+1).
|
||||
'.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
|
||||
}
|
||||
$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 .= '<Relationship Id="rId'.($i+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"'.
|
||||
' Target="worksheets/sheet'.($i+1).".xml\"/>\n";
|
||||
}
|
||||
$s .= '<Relationship Id="rId'.($i+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/></Relationships>';
|
||||
$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 .= '<sheet name="' . $v['name'] . '" sheetId="' . ( $k + 1) . '" state="visible" r:id="rId' . ( $k + 2) . '"/>';
|
||||
}
|
||||
$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 = '<si><t>'.implode("</t></si>\r\n<si><t>", $this->SI).'</t></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 .= '<c r="' . $cname . '"'.($ct ? ' t="'.$ct.'"' : '').($cs ? ' s="'.$cs.'"' : '').'>'
|
||||
.($ct === 'inlineStr' ? '<is><t>'.$cv.'</t></is>' : '<v>' . $cv . '</v>')."</c>\r\n";
|
||||
.($ct === 'inlineStr' ? '<is><t>'.$cv.'</t></is>' : '<v>' . $cv . '</v>')."</c>\r\n";
|
||||
}
|
||||
$ROWS[] = $row . "</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 .= '<Override PartName="/xl/worksheets/sheet'.($i+1).
|
||||
'.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>';
|
||||
}
|
||||
$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 .= '<Relationship Id="rId'.($i+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet"'.
|
||||
' Target="worksheets/sheet'.($i+1).".xml\"/>\n";
|
||||
}
|
||||
$s .= '<Relationship Id="rId'.($i+2).'" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/sharedStrings" Target="sharedStrings.xml"/></Relationships>';
|
||||
$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 .= '<sheet name="'.$this->sheets[$i]['Name'].'" sheetId="'.($i+1).'" state="visible" r:id="rId'.($i+2).'"/>';
|
||||
}
|
||||
$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 = '<si><t>'.implode("</t></si>\r\n<si><t>", $this->SI).'</t></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) {
|
||||
|
Loading…
Reference in New Issue
Block a user